

<!DOCTYPE html>
<html lang="zh-CN" data-default-color-scheme=auto>



<head>
  <meta charset="UTF-8">
  <link rel="apple-touch-icon" sizes="76x76" href="/hello_lixi.gitee.io/img/fluid.png">
  <link rel="icon" href="/hello_lixi.gitee.io/img/fluid.png">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, shrink-to-fit=no">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  
  <meta name="theme-color" content="#2f4154">
  <meta name="author" content="Mr.LI">
  <meta name="keywords" content="">
  
    <meta name="description" content="Java线程池 springboot增删改查单体程序 Nacos Mybatis-Plus Redis-Redisson">
<meta property="og:type" content="article">
<meta property="og:title" content="2022.11-2023.1 Java开发学习">
<meta property="og:url" content="https://xistudycode.gitee.io/hello_lixi.gitee.io/2023/02/14/%E5%AD%A6%E4%B9%A0%E4%BB%BB%E5%8A%A1%E6%80%BB%E7%BB%93/index.html">
<meta property="og:site_name" content="Welcome to the technology blog">
<meta property="og:description" content="Java线程池 springboot增删改查单体程序 Nacos Mybatis-Plus Redis-Redisson">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="c:/Users/李曦/AppData/Roaming/Typora/typora-user-images/image-20221227113336543.png">
<meta property="og:image" content="c:/Users/李曦/Desktop/Java开发笔记/图库/springbootcrud数据库关系图.png">
<meta property="og:image" content="c:/Users/李曦/Desktop/Java开发笔记/图库/springbootcrud项目结构.png">
<meta property="og:image" content="https://raw.githubusercontent.com/hellelixi/note-image01/main/images/%E6%9F%A5%E8%AF%A2%E9%83%A8%E9%97%A8%E6%89%80%E5%B1%9E%E5%91%98%E5%B7%A51.png">
<meta property="og:image" content="https://raw.githubusercontent.com/hellelixi/note-image01/main/images/image-20221228162723597.png">
<meta property="og:image" content="https://raw.githubusercontent.com/hellelixi/note-image01/main/images/image-20221228162853378.png">
<meta property="og:image" content="c:/Users/李曦/AppData/Roaming/Typora/typora-user-images/image-20221229165650508.png">
<meta property="og:image" content="https://raw.githubusercontent.com/hellelixi/note-image01/main/images/image-20230109091258572.png">
<meta property="og:image" content="https://raw.githubusercontent.com/hellelixi/note-image01/main/images/image-20230110164002626.png">
<meta property="og:image" content="c:/Users/李曦/AppData/Roaming/Typora/typora-user-images/image-20230110163316611.png">
<meta property="og:image" content="https://raw.githubusercontent.com/hellelixi/note-image01/main/images/image-20230110163043159.png">
<meta property="og:image" content="https://raw.githubusercontent.com/hellelixi/note-image01/main/images/9bcb7f4a7b7c48349ab48bad7ca4f425.png">
<meta property="og:image" content="https://raw.githubusercontent.com/hellelixi/note-image01/main/images/redission1.png">
<meta property="og:image" content="https://xistudycode.gitee.io/hello_lixi.gitee.io/2023/02/14/%E5%AD%A6%E4%B9%A0%E4%BB%BB%E5%8A%A1%E6%80%BB%E7%BB%93/%E5%9B%BE%E5%BA%93/image-20230118165823574.png">
<meta property="og:image" content="https://raw.githubusercontent.com/hellelixi/note-image01/main/images/image-20230118165849107.png">
<meta property="og:image" content="https://raw.githubusercontent.com/hellelixi/note-image01/main/images/image-20230118165540511-1676358792705.png">
<meta property="article:published_time" content="2023-02-14T06:32:47.113Z">
<meta property="article:modified_time" content="2023-02-14T07:18:38.849Z">
<meta property="article:author" content="Mr.LI">
<meta property="article:tag" content="Java开发学习">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="c:/Users/李曦/AppData/Roaming/Typora/typora-user-images/image-20221227113336543.png">
  
  
    <meta name="referrer" content="no-referrer-when-downgrade">
  
  
  <title>2022.11-2023.1 Java开发学习 - Welcome to the technology blog</title>

  <link  rel="stylesheet" href="https://lib.baomitu.com/twitter-bootstrap/4.6.1/css/bootstrap.min.css" />



  <link  rel="stylesheet" href="https://lib.baomitu.com/github-markdown-css/4.0.0/github-markdown.min.css" />

  <link  rel="stylesheet" href="https://lib.baomitu.com/hint.css/2.7.0/hint.min.css" />

  <link  rel="stylesheet" href="https://lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.css" />



<!-- 主题依赖的图标库，不要自行修改 -->
<!-- Do not modify the link that theme dependent icons -->

<link rel="stylesheet" href="//at.alicdn.com/t/font_1749284_hj8rtnfg7um.css">



<link rel="stylesheet" href="//at.alicdn.com/t/font_1736178_lbnruvf0jn.css">


<link  rel="stylesheet" href="/hello_lixi.gitee.io/css/main.css" />


  <link id="highlight-css" rel="stylesheet" href="/hello_lixi.gitee.io/css/highlight.css" />
  
    <link id="highlight-css-dark" rel="stylesheet" href="/hello_lixi.gitee.io/css/highlight-dark.css" />
  




  <script id="fluid-configs">
    var Fluid = window.Fluid || {};
    Fluid.ctx = Object.assign({}, Fluid.ctx)
    var CONFIG = {"hostname":"xistudycode.gitee.io","root":"/hello_lixi.gitee.io/","version":"1.9.3","typing":{"enable":true,"typeSpeed":70,"cursorChar":"_","loop":false,"scope":[]},"anchorjs":{"enable":true,"element":"h1,h2,h3,h4,h5,h6","placement":"left","visible":"hover","icon":""},"progressbar":{"enable":true,"height_px":3,"color":"#29d","options":{"showSpinner":false,"trickleSpeed":100}},"code_language":{"enable":true,"default":"TEXT"},"copy_btn":true,"image_caption":{"enable":true},"image_zoom":{"enable":true,"img_url_replace":["",""]},"toc":{"enable":true,"placement":"right","headingSelector":"h1,h2,h3,h4,h5,h6","collapseDepth":0},"lazyload":{"enable":true,"loading_img":"/img/loading.gif","onlypost":false,"offset_factor":2},"web_analytics":{"enable":true,"follow_dnt":true,"baidu":null,"google":null,"gtag":null,"tencent":{"sid":null,"cid":null},"woyaola":null,"cnzz":null,"leancloud":{"app_id":null,"app_key":null,"server_url":null,"path":"window.location.pathname","ignore_local":false}},"search_path":"/hello_lixi.gitee.io/local-search.xml"};

    if (CONFIG.web_analytics.follow_dnt) {
      var dntVal = navigator.doNotTrack || window.doNotTrack || navigator.msDoNotTrack;
      Fluid.ctx.dnt = dntVal && (dntVal.startsWith('1') || dntVal.startsWith('yes') || dntVal.startsWith('on'));
    }
  </script>
  <script  src="/hello_lixi.gitee.io/js/utils.js" ></script>
  <script  src="/hello_lixi.gitee.io/js/color-schema.js" ></script>
  

  

  

  

  

  

  

  



  
<meta name="generator" content="Hexo 5.4.2"></head>


<body>
  

  <header>
    

<div class="header-inner" style="height: 70vh;">
  <nav id="navbar" class="navbar fixed-top  navbar-expand-lg navbar-dark scrolling-navbar">
  <div class="container">
    <a class="navbar-brand" href="/hello_lixi.gitee.io/">
      <strong>xBlog</strong>
    </a>

    <button id="navbar-toggler-btn" class="navbar-toggler" type="button" data-toggle="collapse"
            data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <div class="animated-icon"><span></span><span></span><span></span></div>
    </button>

    <!-- Collapsible content -->
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav ml-auto text-center">
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/hello_lixi.gitee.io/">
                <i class="iconfont icon-home-fill"></i>
                首页
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/hello_lixi.gitee.io/archives/">
                <i class="iconfont icon-archive-fill"></i>
                归档
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/hello_lixi.gitee.io/categories/">
                <i class="iconfont icon-category-fill"></i>
                分类
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/hello_lixi.gitee.io/tags/">
                <i class="iconfont icon-tags-fill"></i>
                标签
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/hello_lixi.gitee.io/about/">
                <i class="iconfont icon-user-fill"></i>
                关于
              </a>
            </li>
          
        
        
          <li class="nav-item" id="search-btn">
            <a class="nav-link" target="_self" href="javascript:;" data-toggle="modal" data-target="#modalSearch" aria-label="Search">
              &nbsp;<i class="iconfont icon-search"></i>&nbsp;
            </a>
          </li>
          
        
        
          <li class="nav-item" id="color-toggle-btn">
            <a class="nav-link" target="_self" href="javascript:;" aria-label="Color Toggle">&nbsp;<i
                class="iconfont icon-dark" id="color-toggle-icon"></i>&nbsp;</a>
          </li>
        
      </ul>
    </div>
  </div>
</nav>

  

<div id="banner" class="banner" parallax=true
     style="background: url('/hello_lixi.gitee.io/img/default.png') no-repeat center center; background-size: cover;">
  <div class="full-bg-img">
    <div class="mask flex-center" style="background-color: rgba(0, 0, 0, 0.3)">
      <div class="banner-text text-center fade-in-up">
        <div class="h2">
          
            <span id="subtitle" data-typed-text="2022.11-2023.1 Java开发学习"></span>
          
        </div>

        
          
  <div class="mt-3">
    
      <span class="post-meta mr-2">
        <i class="iconfont icon-author" aria-hidden="true"></i>
        Mr.LI
      </span>
    
    
      <span class="post-meta">
        <i class="iconfont icon-date-fill" aria-hidden="true"></i>
        <time datetime="2023-02-14 14:32" pubdate>
          2023年2月14日 下午
        </time>
      </span>
    
  </div>

  <div class="mt-1">
    
      <span class="post-meta mr-2">
        <i class="iconfont icon-chart"></i>
        
          30k 字
        
      </span>
    

    
      <span class="post-meta mr-2">
        <i class="iconfont icon-clock-fill"></i>
        
        
        
          250 分钟
        
      </span>
    

    
    
      
        <span id="busuanzi_container_page_pv" style="display: none">
          <i class="iconfont icon-eye" aria-hidden="true"></i>
          <span id="busuanzi_value_page_pv"></span> 次
        </span>
        
      
    
  </div>


        
      </div>

      
    </div>
  </div>
</div>

</div>

  </header>

  <main>
    
      

<div class="container-fluid nopadding-x">
  <div class="row nomargin-x">
    <div class="side-col d-none d-lg-block col-lg-2">
      

    </div>

    <div class="col-lg-8 nopadding-x-md">
      <div class="container nopadding-x-md" id="board-ctn">
        <div id="board">
          <article class="post-content mx-auto">
            <!-- SEO header -->
            <h1 style="display: none">2022.11-2023.1 Java开发学习</h1>
            
              <p class="note note-info">
                
                  
                    本文最后更新于：2023年2月14日 下午
                  
                
              </p>
            
            
              <div class="markdown-body">
                
                <h3 id="11-7-11-12-Java线程池"><a href="#11-7-11-12-Java线程池" class="headerlink" title="11.7-11.12  Java线程池"></a>11.7-11.12  Java线程池</h3><p>实习任务：Java线程池学习，使用线程池处理10000此并发请求的java application demo</p>
<p>线程池的创建⽅法总共有 7 种，但总体来说可分为 2 类：</p>
<p>一. 通过ThreadPoolExecutor创建线程池，在阿里的开发手册上推荐使用；</p>
<p> ThreadPoolExecutor：最原始的创建线程池的方法。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">new</span> <span class="hljs-title class_">ThreadPoolExecutor</span>(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit, runnableTaskQueue,handler);<br></code></pre></td></tr></table></figure>

<p>1.corePoolSize（线程池的基本大小）：当提交一个任务到线程池时，线程池会创建一个线程来执行任务，即使其他空闲的基本线程能够执行新任务也会创建线程，等到需要执行的任务书大于线程池基本大小时就不再创建。如果调用了线程池的prestartAllCoreThreads()方法，线程池会提前创建并启动所有基本线程。<br>2.maximumPoolSize（线程池最大数量）：线程池允许创建的最大线程数。如果队列满了，并且已创建的线程数小于最大线程数，则线程会在创建新的线程执行任务。如果使用了无界的任务队列这个参数就没什么效果。<br>3.keepAliveTime（线程活动保持时间）：线程池的工作线程空闲后，保持存活的时间。所以，如果任务很多，并且没个任务执行的时间比较短，可以调大时间，提高线程的利用率。<br>4.TimeUnit（线程活动保持时间的单位）：可选的单位有天（DAYS）、小时（HOURS）、分钟（MINUTES）、毫秒（MILLISECONDS）、微妙（MICROSECONDS，千分之一毫秒）和纳秒（NANOSECONDS，千分之一微妙）。<br>5.runnableTaskQueue（任务队列）：用于保存等待执行的任务的阻塞队列。可以选择的阻塞队列有：<br>① ArrayBlockingQueue：是一个基于数组结构的有界阻塞队列，此队列按FIFO（先进先出）原则对元素进行排序。<br>② LinkedBlockingQueue：一个基于链表结构的阻塞队列，此队列按FIFO排序元素，吞吐量通常要高于ArrayBlockingQueue。<br>③ SynchronousQueue：一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作，否做插入操作一直处于阻塞状态，吞吐量通常要高于LinkedBlockingQueue。<br>④PriorityBlockingQueue：一个具有优先级的无限阻塞队列。<br>6.handler（饱和策略）：当队列和线程池都满了，说明线程池处于饱和状态，那么必须采取一种策略处理提交的新任务。这个策略默认是AbortPolicy，表示无法处理新任务时抛出异常。在JDK1.5中java线程池框架提供了4种策略：<br>① AbortPolicy：直接抛出异常。<br>② CallerRunsPolicy：只用调用者所在线程来运行任务。<br>③ DiscardOldestPolicy：丢弃队列里最近的一个任务，并执行当前任务。<br>④ DiscardPolicy：不处理，丢弃掉。<br>也可以实现RejectedExecutionHandler接口自定义策略。</p>
<p>二.通过Executor创建线程池；</p>
<ol>
<li>Executors.newFixedThreadPool：创建⼀个固定⼤⼩的线程池，可控制并发的线程数，超出的线程会在队列中等待；</li>
<li>Executors.newCachedThreadPool：创建⼀个可缓存的线程池，若线程数超过处理所需，缓存⼀段时间后会回收，若线程数不够，则新建线程；</li>
<li>Executors.newSingleThreadExecutor：创建单个线程数的线程池，它可以保证先进先出的执⾏顺序，所有任务的都是被顺序执行，最多会有一个任务处于活动状态，并且不允许使用者改动线程池实例，因此可以避免其改变线程数目；</li>
<li>Executors.newScheduledThreadPool：创建⼀个可以执⾏延迟任务的线程池；</li>
<li>Executors.newSingleThreadScheduledExecutor：创建⼀个单线程的可以执⾏延迟任务的线程池；</li>
<li>Executors.newWorkStealingPool：创建⼀个抢占式执⾏的线程池（任务执⾏顺序不确定）（JDK1.8 添加）其内部会构建ForkJoinPool，利用Work-Stealing算法，并行地处理任务，不保证处理顺序。</li>
</ol>
<p>不建议使用Executor去创建线程池，FixedThreadPool和SingleTreadPool，允许的请求队列长度为Integer.MAX_VALUE，可能会堆积大量的请求，从而导致OOM；</p>
<p>CachedTreadPool，允许创建的的线程数量为Integer.MAX_VALUE，肯会创建大量的线程，从而导致OOM；</p>
<p>CountDownLatch的作用很简单，就是一个或者一组线程在开始执行操作之前，必须要等到其他线程执行完才可以。我们举一个例子来说明，在考试的时候，老师必须要等到所有人交了试卷才可以走。此时老师就相当于等待线程，而学生就好比是执行的线程。</p>
<p>注意：java中还有一个同步工具类叫做CyclicBarrier，他的作用和CountDownLatch类似。同样是等待其他线程都完成了，才可以进行下一步操作，我们再举一个例子，在打王者的时候，在开局前所有人都必须要加载到100%才可以进入。否则所有玩家都相互等待。</p>
<p>区别：</p>
<p>CountDownLatch: 一个线程(或者多个)， 等待另外N个线程完成某个事情之后才能执行。 CyclicBarrier : N个线程相互等待，任何一个线程完成之前，所有的线程都必须等待。关键点其实就在于那N个线程（1）CountDownLatch里面N个线程就是学生，学生做完了试卷就可以走了，不用等待其他的学生是否完成（2）CyclicBarrier 里面N个线程就是所有的游戏玩家，一个游戏玩家加载到100%还不可以，必须要等到其他的游戏玩家都加载到100%才可以开局。</p>
<p><strong>validator</strong>是一个字符串验证和过滤库，能够做到我们常规应用开发中各种验证方式，比如是否包含某个字符串，字符串的长度类型等等，完全能满足我们日常的前端开发，并且还可以在服务端使用。</p>
<p>代码：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">TreadPoolTest</span> &#123;<br><br>    <span class="hljs-keyword">static</span> <span class="hljs-type">int</span> num=<span class="hljs-number">1</span>;<br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> &#123;<br><br>        ThreadPoolExecutor executor=<span class="hljs-keyword">new</span> <span class="hljs-title class_">ThreadPoolExecutor</span>(<span class="hljs-number">6</span>,<span class="hljs-number">12</span>, <span class="hljs-number">2</span>,TimeUnit.MINUTES,<br>                <span class="hljs-keyword">new</span> <span class="hljs-title class_">LinkedBlockingDeque</span>&lt;&gt;(<span class="hljs-number">10000</span>));<br>        CountDownLatch latch=<span class="hljs-keyword">new</span> <span class="hljs-title class_">CountDownLatch</span>(<span class="hljs-number">1</span>);<br><br>        <span class="hljs-type">int</span> requestNum=<span class="hljs-number">10000</span>;<br>        <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i &lt; requestNum; i++) &#123;<br>            requestHandle handle=<span class="hljs-keyword">new</span> <span class="hljs-title class_">requestHandle</span>(latch);<br>            executor.execute(handle);<br>        &#125;<br>        latch.countDown();<br>        executor.shutdown();<br><br>    &#125;<br>    <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">requestHandle</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Runnable</span>&#123;<br><br>        CountDownLatch latch;<br>        <span class="hljs-keyword">public</span> <span class="hljs-title function_">requestHandle</span><span class="hljs-params">(CountDownLatch latch)</span>&#123;<br>            <span class="hljs-built_in">this</span>.latch=latch;<br><br>        &#125;<br>        <span class="hljs-meta">@Override</span><br>        <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">run</span><span class="hljs-params">()</span> &#123;<br>            <span class="hljs-type">long</span> startTime=<span class="hljs-number">0</span>;<br><br>            <span class="hljs-keyword">try</span> &#123;<br>                <span class="hljs-comment">//startTime=System.currentTimeMillis();</span><br>                latch.await();<br>                <span class="hljs-comment">//System.out.println(&quot;begin test===========&quot;);</span><br>                System.out.println(num++);<br><br>            &#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;<br>                e.printStackTrace();<br>            &#125;<br>           <span class="hljs-comment">// long endTime=System.currentTimeMillis();</span><br>           <span class="hljs-comment">// long t=endTime-startTime;</span><br>            <span class="hljs-comment">//System.out.println(&quot;Missions continue &quot;+t /1000F +&quot;second&quot;);</span><br>        &#125;<br>    &#125;<br>    <br>&#125;<br></code></pre></td></tr></table></figure>





<h3 id="11-14-11-19-增删改查简单的单体程序"><a href="#11-14-11-19-增删改查简单的单体程序" class="headerlink" title="11.14-11.19 增删改查简单的单体程序"></a>11.14-11.19 增删改查简单的单体程序</h3><p>Spring Boot 学习，使用springboot完成员工信息的增删改查简单的单体程序。</p>
<ol>
<li>使用springboot install初始化项目，在数据库中创建员工信息表Staff，设置主键id自增，包含name, age, tel, department 属性。</li>
<li>导入springboot相关依赖，数据库mysql依赖，数据持久化依赖mybatis-plus，以及lombok依赖。</li>
<li>创建项目配置文件，配置项目端口号等信息，配置数据库连接信息；</li>
<li>在项目中创建员工实体类，映射数据库表Staff，主键id自增。</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@TableName(&quot;staff&quot;)</span><br><span class="hljs-meta">@Data</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Staff</span> &#123;<br><br>    <span class="hljs-meta">@TableId(value = &quot;id&quot;,type = IdType.AUTO)</span><br>    <span class="hljs-keyword">private</span> <span class="hljs-type">int</span> id;<br><br>    <span class="hljs-meta">@TableField(&quot;name&quot;)</span><br>    <span class="hljs-keyword">private</span> String name;<br><br>    <span class="hljs-meta">@TableField(&quot;age&quot;)</span><br>    <span class="hljs-keyword">private</span> String age;<br><br>    <span class="hljs-meta">@TableField(&quot;tel&quot;)</span><br>    <span class="hljs-keyword">private</span> String tel;<br><br>    <span class="hljs-meta">@TableField(&quot;departName&quot;)</span><br>    <span class="hljs-keyword">private</span> String departName;<br><br>&#125;<br></code></pre></td></tr></table></figure>

<ol start="5">
<li>创建mapper文件，继承BaseMapper类，继承该接口后，无需编写 mapper.xml 文件，即可获得CRUD功能。</li>
<li>创建service，实现员工信息的增删改查功能；</li>
<li>创建controller，完成员工信息的增删改查简单的单体程序。</li>
</ol>
<p>版本控制（Revision control）是一种在开发的过程中用于管理我们对文件、目录或工程等内 容的修改历史，方便查看更改历史记录，备份以便恢复以前的版本的软件工程技术。常见的版本管理工具有git，svn，cvs等，用的较多的是git和svn。</p>
<p>SVN是所有的版本数据都保存在服务器上，协同开发者从服务器上同步更新或上传自己的修改，所有的版本数据都存在服务器上，用户的本地只有自己以前所同步的版本，如果不连网的 话，用户就看不到历史版本，也无法切换版本验证问题，或在不同分支工作。而且，所有数 据都保存在单一的服务器上，有很大的风险这个服务器会损坏，这样就会丢失所有的数据。</p>
<p>Git所有版本信息仓库全部同步到本地的每个用户，这样就可以在本地查看所有版本历史，可以 离线在本地提交，只需在连网时push到相应的服务器或其他用户那里。由于每个用户那里保存的都是所有的版本数据，只要有一个用户的设备没有问题就可以恢复所有的数据，但这增加了本地存储空间的占用。</p>
<p>1.下载安装git程序；</p>
<p>2.绑定自己的用户名和邮箱；</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs git">git config --global user.name &quot;&quot; <br>git config --global user.email  <br></code></pre></td></tr></table></figure>

<p>创建本地仓库，git init，或者克隆远程项目，git clone url</p>
<p>文件的四种状态：</p>
<p>Untracked: 未跟踪, 此文件在文件夹中, 但并没有加入到git库, 不参与版本控制. 通过git add 状态变为Staged.<br>Unmodify: 文件已经入库, 未修改, 即版本库中的文件快照内容与文件夹中完全一致. 这种 类型的文件有两种去处, 如果它被修改, 而变为Modified. 如果使用git rm移出版本库, 则成 为Untracked文件</p>
<p>Modified: 文件已修改, 仅仅是修改, 并没有进行其他的操作. 这个文件也有两个去处, 通过 git add可进入暂存staged状态, 使用git checkout 则丢弃修改过, 返回到unmodify状态, 这 个git checkout即从库中取出文件, 覆盖当前修改 !<br>Staged: 暂存状态. 执行git commit则将修改同步到库中, 这时库中的文件和本地文件又变 为一致, 文件为Unmodify状态. 执行git reset HEAD filename取消暂存, 文件状态为 Modified</p>
<p>3.设置本机绑定SSH公钥，登录远程gitee仓库；</p>
<p>常用git命令</p>
<p>add .</p>
<p>commit   -m “ xiaoxi”</p>
<p>push</p>
<h3 id="11-21-11-26-Nacos注册中心"><a href="#11-21-11-26-Nacos注册中心" class="headerlink" title="11.21-11.26 Nacos注册中心"></a>11.21-11.26 Nacos注册中心</h3><p>Nacos注册中心学习，完成两个Spring Boot在注册中心注册</p>
<p>Nacos一个更易于构建云原生应用的动态服务发现(Nacos Discovery )、服务配置(Nacos Config)和服务管理平台.</p>
<p>核心功能<br>Nacos Discovery<br>服务注册：Nacos Client会通过发送REST请求的方式向Nacos Server注册自己的服务，提供自身的元数据，比如ip地 址、端口等信息。Nacos Server接收到注册请求后，就会把这些元数据信息存储在一个双层的内存Map中。<br>服务心跳：在服务注册后，Nacos Client会维护一个定时心跳来持续通知Nacos Server，说明服务一直处于可用状态，防 止被剔除。默认5s发送一次心跳。<br>服务同步：Nacos Server集群之间会互相同步服务实例，用来保证服务信息的一致性。  leader    raft<br>服务发现：服务消费者（Nacos Client）在调用服务提供者的服务时，会发送一个REST请求给Nacos Server，获取上面 注册的服务清单，并且缓存在Nacos Client本地，同时会在Nacos Client本地开启一个定时任务定时拉取服务端最新的注 册表信息更新到本地缓存<br>服务健康检查：Nacos Server会开启一个定时任务用来检查注册服务实例的健康情况，对于超过15s没有收到客户端心跳 的实例会将它的healthy属性置为false(客户端服务发现时不会发现)，如果某个实例超过30秒没有收到心跳，直接剔除该 实例(被剔除的实例如果恢复发送心跳则会重新注册)</p>
<ol>
<li><p>下载安装nacos，解压，进入nacos目录，启动nacos</p>
</li>
<li><p>复制nacos地址，访问nocas的管理端 ，默认的用户名密码是 nocas/nocas</p>
</li>
<li><p>搭建Nacos-client服务，引入依赖，父Pom中支持spring cloud&amp;spring cloud alibaba, 引入依赖springcloud的版本，在子项目中导入依赖spring‐cloud‐starter‐alibaba‐nacos‐discovery</p>
</li>
<li><p>application.properties中配置nacos的地址</p>
<p>spring.cloud.nacos.discovery.server‐addr=localhost:8848</p>
</li>
<li><p>启动springboot应用，nacos管理端界面查看是否成功注册</p>
</li>
</ol>
<h3 id="11-28-12-03-RPC服务调用"><a href="#11-28-12-03-RPC服务调用" class="headerlink" title="11.28-12.03 RPC服务调用"></a>11.28-12.03 RPC服务调用</h3><p>RPC服务调用学习，NACOS注册中心的A和B应用，实现A通过Dubbo RPC的方式调用B提供的接口</p>
<p>掌握禅道基本操作流程</p>
<p>Dubbo是阿里巴巴公司开源的一个高性能、轻量级的 Java <a target="_blank" rel="noopener" href="https://so.csdn.net/so/search?q=RPC&spm=1001.2101.3001.7020">RPC</a> 框架，致力于提供高性能和透明化的 RPC 远程服务调用方案，以及 SOA 服务治理方案。</p>
<p>节点角色说明</p>
<p>1.Provider 暴露服务的服务提供方<br>2.Consumer 调用远程服务的服务消费方<br>3.Registry 服务注册与发现的注册中心<br>4.Monitor 统计服务的调用次数和调用时间的监控中心<br>5.Container 服务运行容器</p>
<p>调用关系说明</p>
<p>1.服务容器负责启动，加载，运行服务提供者。<br>2.服务提供者在启动时，向注册中心注册自己提供的服务。<br>3.服务消费者在启动时，向注册中心订阅自己所需的服务。<br>4.注册中心返回服务提供者地址列表给消费者，如果有变更，注册中心将基于长连接推送变更数据给消费者。<br>5.服务消费者，从提供者地址列表中，基于软负载均衡算法，选一台提供者进行调用，如果调用失败，再选另一台调用。<br>6.服务消费者和提供者，在内存中累计调用次数和调用时间，定时每分钟发送一次统计数据到监控中心。</p>
<p>项目结构</p>
<p>demo-dubbo-nacos-api 服务api接口</p>
<p>demo-dubbo-nacos-provider 服务提供者</p>
<p>demo-dubbo-nacos-consume 服务消费者</p>
<p>导入相关依赖，分别配置provider，consume的yml配置文件，</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs yml"><span class="hljs-attr">spring:</span><br>  <span class="hljs-attr">application:</span><br>    <span class="hljs-comment"># 应用名称</span><br>    <span class="hljs-attr">name:</span> <span class="hljs-string">demo-dubbo-nacos-consumer</span><br>  <span class="hljs-comment"># 配置 Nacos 注册中心</span><br>  <span class="hljs-attr">cloud:</span><br>    <span class="hljs-attr">nacos:</span><br>      <span class="hljs-attr">discovery:</span><br>        <span class="hljs-attr">server-addr:</span> <span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span><span class="hljs-string">:8848</span><br><span class="hljs-attr">dubbo:</span><br>  <span class="hljs-attr">application:</span><br>    <span class="hljs-attr">name:</span> <span class="hljs-string">demo-dubbo-nacos-consumer</span><br>  <span class="hljs-attr">cloud:</span><br>    <span class="hljs-comment"># 用于消费者订阅提供方的应用名称列表</span><br>    <span class="hljs-attr">subscribed-services:</span> <span class="hljs-string">demo-dubbo-nacos-provider</span><br>  <span class="hljs-attr">scan:</span><br>    <span class="hljs-attr">base-packages:</span> <span class="hljs-string">com.daobili.provider</span><br></code></pre></td></tr></table></figure>



<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs yml"><span class="hljs-attr">dubbo:</span><br>  <span class="hljs-comment"># 提供方应用信息，用于计算依赖关系</span><br>  <span class="hljs-attr">application:</span><br>    <span class="hljs-attr">name:</span> <span class="hljs-string">demo-dubbo-nacos-provider</span><br>  <span class="hljs-comment"># 扫描需要暴露的服务，可以被 @EnableDubbo 注解替代</span><br>  <span class="hljs-attr">scan:</span><br>    <span class="hljs-attr">base-packages:</span> <span class="hljs-string">com.daobili.provider</span><br><br>  <span class="hljs-comment"># 用 dubbo 协议在 20880 端口暴露服务</span><br>  <span class="hljs-attr">protocol:</span><br>    <span class="hljs-attr">name:</span> <span class="hljs-string">dubbo</span><br>    <span class="hljs-attr">port:</span> <span class="hljs-number">20880</span><br>  <span class="hljs-comment"># 使用 nacos 注册中心暴露服务地址</span><br>  <span class="hljs-attr">registry:</span><br>    <span class="hljs-attr">protocol:</span> <span class="hljs-string">nacos</span><br>    <span class="hljs-attr">address:</span> <span class="hljs-string">spring-cloud://localhost</span><br></code></pre></td></tr></table></figure>

<p>使用时在相应的地方加上@Reference</p>
<p>@EnableDubbo 注解在启动类上替代</p>
<p> scan:<br>    base-packages: com.daobili.provider</p>
<p>第二种配置：</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs yml"><span class="hljs-attr">dubbo:</span><br>  <span class="hljs-attr">application:</span><br>    <span class="hljs-attr">name:</span> <span class="hljs-string">demo-dubbo-nacos-consumer</span><br>    <span class="hljs-attr">id:</span> <span class="hljs-string">demo-dubbo-nacos-consumer</span><br>    <span class="hljs-comment">#需要配置id，不然会报错</span><br>  <span class="hljs-attr">cloud:</span><br>    <span class="hljs-comment"># 用于消费者订阅提供方的应用名称列表</span><br>    <span class="hljs-attr">subscribed-services:</span> <span class="hljs-string">demo-dubbo-nacos-provider</span><br></code></pre></td></tr></table></figure>

<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs yml"><span class="hljs-attr">dubbo:</span><br>  <span class="hljs-comment"># 提供方应用信息，用于计算依赖关系</span><br>  <span class="hljs-attr">application:</span><br>    <span class="hljs-attr">name:</span> <span class="hljs-string">demo-dubbo-nacos-provider</span><br>    <span class="hljs-attr">id:</span> <span class="hljs-string">demo-dubbo-nacos-provider</span> <br>    <span class="hljs-comment">#需要配置id，不然会报错</span><br><br>  <span class="hljs-comment"># 用 dubbo 协议在 20880 端口暴露服务</span><br>  <span class="hljs-attr">protocol:</span><br>    <span class="hljs-attr">name:</span> <span class="hljs-string">dubbo</span><br>    <span class="hljs-attr">port:</span> <span class="hljs-number">20880</span><br>  <span class="hljs-comment"># 使用 nacos 注册中心暴露服务地址</span><br>  <span class="hljs-attr">registry:</span><br>    <span class="hljs-attr">protocol:</span> <span class="hljs-string">nacos</span><br>    <span class="hljs-attr">address:</span> <span class="hljs-string">spring-cloud://localhost</span><br></code></pre></td></tr></table></figure>







<h3 id="12-05-12-10-Open-Feign的方式调"><a href="#12-05-12-10-Open-Feign的方式调" class="headerlink" title="12.05-12.10 Open Feign的方式调"></a>12.05-12.10 Open Feign的方式调</h3><p>EST服务调用学习，NACOS注册中心的A和B应用，实现A通过Open Feign的方式调用B提供的接口</p>
<p>掌握Docker基本操作</p>
<p>OpenFeign 是 springcloud 在Feign的基础上支持了 SpringMVC 的注解，如 @RequestMapping 等等。OpenFeign 的 @FeignClient 可以解析 SpringMVC 的 @RequestMapping 注解下的接口，并通过动态代理的方式产生实现类，实现类中做负载均衡并调用其他服务。</p>
<p>使用openfeign：</p>
<p>1.添加openfeign的依赖</p>
<figure class="highlight ebnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ebnf"><span class="hljs-attribute">spring-cloud-starter-openfeign</span><br></code></pre></td></tr></table></figure>

<p>2.创建openfeign接口，方法名和参数都需要和服务提供者者保持一致，@FeignClient(name = “employee-service”,path = “/employee”)中的就是服务提供者对应的全路径，将服务提供者的controller方法在接口中声明；</p>
<p>3.在启动类上，@EnableFeignClients(“com.xcmg.manage.feign”)，申明该项目是Feign客户端，扫描对应的feign client。</p>
<p>4.在消费者的controller中就可以openfeign接口进行使用。</p>
<h3 id="12-12-12-17-BatisPlus-注解方式数据库映射和增删改查"><a href="#12-12-12-17-BatisPlus-注解方式数据库映射和增删改查" class="headerlink" title="12.12-12.17 BatisPlus 注解方式数据库映射和增删改查"></a>12.12-12.17 BatisPlus 注解方式数据库映射和增删改查</h3><p>BatisPlus ORM框架学习，实现注解方式的数据库映射和增删改查基本操作</p>
<p>MyBatisPlus ORM框架学习，实现注解方式的数据库映射和增删改查基本操作<br>Mybatis-plus<br>•    无侵入：只做增强不做改变，引入它不会对现有工程产生影响，如丝般顺滑<br>•    损耗小：启动即会自动注入基本 CURD，性能基本无损耗，直接面向对象操作<br>•    强大的 CRUD 操作：内置通用 Mapper、通用 Service，仅仅通过少量配置即可实现单表大部分 CRUD 操作，更有强大的条件构造器，满足各类使用需求<br>•    支持 Lambda 形式调用：通过 Lambda 表达式，方便的编写各类查询条件，无需再担心字段写错<br>•    支持主键自动生成：支持多达4种主键策略（内含分布式唯一ID生成器Sequence），可自由配置，完美解决主键问题<br>•    支持 ActiveRecord 模式：支持 ActiveRecord 形式调用，实体类只需继承 Model 类即可进行强大的 CRUD 操作<br>•    支持自定义全局通用操作：支持全局通用方法注入<br>•    内置代码生成器：采用代码或者Maven插件可快速生成Mapper、Model、Service、Controller 层代码，支持模板引擎，更有超多自定义配置等您来使用<br>•    内置分页插件：基于 MyBatis 物理分页，开发者无需关心具体操作，配置好插件之后，写分页等同于普通 List 查询<br>•    分页插件支持多种数据库：支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库<br>•    内置性能分析插件：可输出 Sql 语句以及其执行时间，建议开发测试时启用该功能，能快速揪出慢查询<br>•    内置全局拦截插件：提供全表 delete 、 update 操作智能分析阻断，也可自定义拦截规则，预防误操作<br>代码练习：springboot中集成Mybatis-plus完成员工信息的增删改查功能。<br>项目结构</p>
<p><img src="C:/Users/李曦/AppData/Roaming/Typora/typora-user-images/image-20221227113336543.png" srcset="/hello_lixi.gitee.io/img/loading.gif" lazyload alt="image-20221227113336543"></p>
<p>1.导入Mybatis-plus依赖</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java">&lt;dependency&gt;<br>    &lt;groupId&gt;com.baomidou&lt;/groupId&gt;<br>    &lt;artifactId&gt;mybatis-plus-boot-starter&lt;/artifactId&gt;<br>    &lt;version&gt;<span class="hljs-number">3.4</span><span class="hljs-number">.2</span>&lt;/version&gt;<br>&lt;/dependency&gt;<br></code></pre></td></tr></table></figure>

<p>2.创建实体 entity–staff</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@TableName(&quot;staff&quot;)</span><br><span class="hljs-meta">@Data</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Staff</span> &#123;<br><br>   <span class="hljs-meta">@TableId(value = &quot;id&quot;,type = IdType.AUTO)</span><br>   <span class="hljs-keyword">private</span> <span class="hljs-type">int</span> id;<br><br>   <span class="hljs-meta">@TableField(&quot;name&quot;)</span><br>   <span class="hljs-keyword">private</span> String name;<br><br>   <span class="hljs-meta">@TableField(&quot;age&quot;)</span><br>   <span class="hljs-keyword">private</span> String age;<br><br>   <span class="hljs-meta">@TableField(&quot;tel&quot;)</span><br>   <span class="hljs-keyword">private</span> String tel;<br><br>   <span class="hljs-meta">@TableField(&quot;departName&quot;)</span><br>   <span class="hljs-keyword">private</span> String departName;<br><br>&#125;<br></code></pre></td></tr></table></figure>

<p>3.创建mapper文件 mapper–StaffyMapper</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Repository</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">StaffMapper</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">BaseMapper</span>&lt;Staff&gt; &#123;<br><span class="hljs-comment">//继承BaseMapper,继承该接口后，无需编写 mapper.xml 文件，即可获得CRUD功能</span><br><br><br>&#125;<br></code></pre></td></tr></table></figure>



<p>4.创建service，开始使用</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Resource</span><br><br>StaffMapper staffMapper;<br></code></pre></td></tr></table></figure>

<p>并且在启动类中扫描mapper文件<br>实体类中的@TableName用来映射数据库中的实体表，@ TableField用来映射数据库的属性。</p>
<p>Mybatis在使时的条件构造器，wrapper，可以设置查询、删除、更新的条件，<br><a target="_blank" rel="noopener" href="https://blog.csdn.net/qq_50975965/article/details/121384866">https://blog.csdn.net/qq_50975965/article/details/121384866</a></p>
<p>Navicat的使用<br>学习使用Navicat创建数据库、数据表，在软件中使用sql语句，转储SQL文件等操作。</p>
<h3 id="12-19-12-24-MyBatisPlus解方式的一对多，多对多等关联表查询"><a href="#12-19-12-24-MyBatisPlus解方式的一对多，多对多等关联表查询" class="headerlink" title="12.19-12.24 MyBatisPlus解方式的一对多，多对多等关联表查询"></a>12.19-12.24 MyBatisPlus解方式的一对多，多对多等关联表查询</h3><p>MyBatisPlus ORM框架学习，实现注解方式的一对多，多对多等关联表查询</p>
<p>在之前springbootcrud项目上进行修改，使用MyBatisPlus对员工进行查询，员工拥有id,name,age,tel,state属性，部门拥有id,remark,name,leader属性，一个员工属于一个部门，一个部门拥有多个员工。</p>
<img src="C:/Users/李曦/Desktop/Java开发笔记/图库/springbootcrud数据库关系图.png" srcset="/hello_lixi.gitee.io/img/loading.gif" lazyload alt="springbootcrud数据库关系图"  />

<p>项目结构</p>
<img src="C:/Users/李曦/Desktop/Java开发笔记/图库/springbootcrud项目结构.png" srcset="/hello_lixi.gitee.io/img/loading.gif" lazyload alt="springbootcrud项目结构" style="zoom:80%;" />

<p>MyBatisPlus一对多查询代码：查询部门所属的所有员工</p>
<p>1.在Depart实体类中添加stafflist属性存储查找到的员工信息</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">private</span> List&lt;Staff&gt; staffList;<br></code></pre></td></tr></table></figure>

<p>2.新建StaffMapper，用于子查询，通过员工表和关系表查找员工信息</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//获取返回部门所属员工信息，.*是选取depart部门信息，像联合查询</span><br><span class="hljs-meta">@Select(&quot;select staff.* from staff_depart,staff where staff_depart.depart_id = #&#123;depart_id&#125; and staff.id = staff_depart.staff_id&quot;)</span><br>List&lt;Staff&gt; <span class="hljs-title function_">returnDepartStaffList</span><span class="hljs-params">(<span class="hljs-meta">@Param(&quot;depart_id&quot;)</span> <span class="hljs-type">int</span> id)</span>;<br></code></pre></td></tr></table></figure>

<p>3.新建DepartMapper主查询，</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//查询部门所属的员工信息</span><br><span class="hljs-meta">@Select(&quot;select * from depart where depart.id = #&#123;id&#125;&quot;)</span><br><span class="hljs-meta">@Results(&#123;</span><br><span class="hljs-meta">//将数据库表中的列和实体类中的属性对应起来，不写最后查出的结果id没有值</span><br><span class="hljs-meta">        @Result(column = &quot;id&quot;, property = &quot;id&quot;,id = true),</span><br><span class="hljs-meta">        @Result(column=&quot;id&quot;,  // 主表中的列名，这个列的值要作为查询参数传给子查询</span><br><span class="hljs-meta">                //代表子查询的查询结果关联的实体属性,将查询到的结果存入staffList</span><br><span class="hljs-meta">                property=&quot;staffList&quot;,  </span><br><span class="hljs-meta">                javaType = List.class, //javaType： 子查询中的返回值类型</span><br><span class="hljs-meta">                many = @Many(select=&quot;com.zks.springbootcrud.mapper.StaffMapper.returnDepartStaffList&quot;)</span><br><span class="hljs-meta">  //@Many： 一对多、多对多使用该注解指定子查询接口方法</span><br><span class="hljs-meta"> //many = @Many(select = “com.neu.mybatisplus.dao.CarDao.returnCars”) 指定子查询接口方法</span><br><span class="hljs-meta"> //一句话版本：用column作为子查询的参数，将子查询返回值写入property</span><br><span class="hljs-meta">        )</span><br><span class="hljs-meta">&#125;)</span><br>Depart <span class="hljs-title function_">getDepartStaffList</span><span class="hljs-params">(<span class="hljs-meta">@Param(&quot;id&quot;)</span> <span class="hljs-type">int</span> id)</span>;<br></code></pre></td></tr></table></figure>

<p>4.controller</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//获取部门所属的所有员工信息</span><br><span class="hljs-meta">@RequestMapping(&quot;/GetDepartmentStaffList/&#123;id&#125;&quot;)</span><br><span class="hljs-keyword">public</span> Depart <span class="hljs-title function_">GetDepartmentStaffList</span><span class="hljs-params">(<span class="hljs-meta">@PathVariable(&quot;id&quot;)</span> <span class="hljs-type">int</span> id)</span>&#123;<br><br>    <span class="hljs-keyword">return</span> departMapper.getDepartStaffList(id);<br>&#125;<br></code></pre></td></tr></table></figure>

<p>5.查询结果</p>
<p><img src="https://raw.githubusercontent.com/hellelixi/note-image01/main/images/%E6%9F%A5%E8%AF%A2%E9%83%A8%E9%97%A8%E6%89%80%E5%B1%9E%E5%91%98%E5%B7%A51.png" srcset="/hello_lixi.gitee.io/img/loading.gif" lazyload alt="查询部门所属员工1"></p>
<p> 意思就是查询主表，并将主表的id这个列和子查询关联起来，将id的值作为查询条件传入子查询中，子查询返回的结果存入到属性Stafflist中.</p>
<p>疑问：在进行查询的时候，查询的数据会绑定到实体类Depart中，这样就会在类中新添加新的属性stafflist，当再有新查询是还会添加新的属性（Staff类中添加额外的属性departlist），那么新查询用不到以前添加 属性时，就会赋空值，当新建StaffVO类，绑定TableName(),但是查询的结果依旧会绑定到实体类中，为什么会这样？</p>
<p>解决：忘了在StaffVO类上加@Data，数据无法赋值和访问</p>
<p>DepartVO</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Data</span>  <span class="hljs-comment">//要加，不然查询结果访问不到，这里可以不用使用@TableName了</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">DepartVO</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">Depart</span> &#123;<br><br>    <span class="hljs-keyword">public</span> List&lt;Staff&gt; staffList;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>DepartMapper</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Mapper</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">DepartMapper</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">BaseMapper</span>&lt;Depart&gt; &#123;<br><br>    <span class="hljs-meta">@Select(&quot;select depart.* from staff_depart,depart where staff_depart.staff_id = #&#123;staff_id&#125; and depart.id = staff_depart.depart_id&quot;)</span> <span class="hljs-comment">//存疑</span><br>    List&lt;Depart&gt; <span class="hljs-title function_">returnDepartment</span><span class="hljs-params">(<span class="hljs-meta">@Param(&quot;staff_id&quot;)</span> String id)</span>;<br><br><br>    <span class="hljs-comment">//查询部门所属的员工信息</span><br>    <span class="hljs-meta">@Select(&quot;select * from depart where depart.id = #&#123;id&#125;&quot;)</span><br>    <span class="hljs-meta">@Results(&#123;</span><br><span class="hljs-meta">           //将数据库表中的列和实体类中的属性对应起来，不写最后查出的结果id没有值</span><br><span class="hljs-meta">            @Result(column = &quot;id&quot;, property = &quot;id&quot;,id = true),</span><br><span class="hljs-meta">            @Result(column=&quot;id&quot;,  // 主表中的列名，这个列的值要作为查询参数传给子查询</span><br><span class="hljs-meta">                    //代表子查询的查询结果关联的实体属性,将查询到的结果存入staffList</span><br><span class="hljs-meta">                    property=&quot;staffList&quot;, </span><br><span class="hljs-meta">                    javaType = List.class, //javaType： 子查询中的返回值类型</span><br><span class="hljs-meta">                    many = @Many(select=&quot;com.zks.springbootcrud.mapper.StaffMapper.returnDepartStaffList&quot;)</span><br><span class="hljs-meta">              //@Many： 一对多、多对多使用该注解指定子查询接口方法</span><br><span class="hljs-meta">     //many = @Many(select = “com.neu.mybatisplus.dao.CarDao.returnCars”) 指定子查询接口方法</span><br><span class="hljs-meta">                    //一句话版本：用column作为子查询的参数，将子查询返回值写入property</span><br><span class="hljs-meta">            )</span><br><span class="hljs-meta">    &#125;)</span><br>    DepartVO <span class="hljs-title function_">getDepartStaffList</span><span class="hljs-params">(<span class="hljs-meta">@Param(&quot;id&quot;)</span> <span class="hljs-type">int</span> id)</span>;<br><br>&#125;<br></code></pre></td></tr></table></figure>

<p>StaffMapper</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Repository</span><br><span class="hljs-meta">@Mapper</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">StaffMapper</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">BaseMapper</span>&lt;Staff&gt; &#123;<br><br><br>    <span class="hljs-meta">@Select(&quot;select * from staff where staff.id = #&#123;id&#125;&quot;)</span><br>    <span class="hljs-meta">@Results(   </span><br><span class="hljs-meta">            @Result(</span><br><span class="hljs-meta">                    column = &quot;id&quot;,  </span><br><span class="hljs-meta">                    property = &quot;departList&quot;, </span><br><span class="hljs-meta">                    javaType = List.class, </span><br><span class="hljs-meta">                    many = @Many(select = &quot;com.zks.springbootcrud.mapper.DepartMapper.returnDepartment&quot;)</span><br><span class="hljs-meta"></span><br><span class="hljs-meta">            )</span><br><span class="hljs-meta">    )</span><br>    Staff_DepartVO <span class="hljs-title function_">selectStaffAndHisDepart</span><span class="hljs-params">(<span class="hljs-meta">@Param(&quot;id&quot;)</span> <span class="hljs-type">int</span> id)</span>;<br><br>    <span class="hljs-comment">//获取返回部门所属员工信息</span><br>    <span class="hljs-meta">@Select(&quot;select staff.* from staff_depart,staff where staff_depart.depart_id = #&#123;depart_id&#125; and staff.id = staff_depart.staff_id&quot;)</span><br>    List&lt;Staff&gt; <span class="hljs-title function_">returnDepartStaffList</span><span class="hljs-params">(<span class="hljs-meta">@Param(&quot;depart_id&quot;)</span> <span class="hljs-type">int</span> id)</span>;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>掌握Xshell、Xftp工具使用</p>
<p>下载和学习使用Xshell、Xftp，</p>
<p>学习了Xshell、Xftp连接的建立、修改等，能够使用Xftp进行文件的操作</p>
<h3 id="12-26-12-30-MyBatisPlu实现多数据源动态切换"><a href="#12-26-12-30-MyBatisPlu实现多数据源动态切换" class="headerlink" title="12.26-12.30 MyBatisPlu实现多数据源动态切换"></a>12.26-12.30 MyBatisPlu实现多数据源动态切换</h3><p>MyBatisPlu实现多数据源动态切换</p>
<p>dynamic-datasource-spring-boot-starter是一个基于springboot的快速集成多数据源的启动器。</p>
<p>特性：</p>
<p>-支持<strong>数据源分组</strong>，适用于多种场景纯粹多库读写分离一主多从混合模式。</p>
<p>-支持数据库敏感配置信息<strong>加密</strong>ENC。</p>
<p>-支持每个数据库独立初始化表结构schema和数据库database。</p>
<p>-支持无数据源启动，支持懒加载数据源（需要的时候再创建连接）。</p>
<p>-支持<strong>自定义注解</strong>，需继承DS3.2.0+。</p>
<p>-提供并简化对Druid，HikariCp，BeeCp,Dbcp2的快速集成。</p>
<p>-提供对Mybatis-Plus，Quartz，ShardingJdbc，P6sy，Jndi等组件的集成方案。</p>
<p>-提供<strong>自定义数据源来源</strong>方案（如全从数据库加载）。</p>
<p>-提供项目启动后<strong>动态增加移除数据源</strong>方案。</p>
<p>-提供Mybatis环境下的<strong>纯读写分离</strong>方案。</p>
<p>提供使用<strong>spel动态参数</strong>解析数据源方案。内置spel，session，header，支持自定义。</p>
<p>-支持<strong>多层数据源嵌套切换</strong>。（ServiceA&gt;&gt;&gt;ServiceB&gt;&gt;&gt;ServiceC）。</p>
<p>-提供**基于seata的分布式事务方案。</p>
<p>-提供<strong>本地多数据源事务方案。</strong></p>
<p>约定</p>
<p>1.本框架只做<strong>切换数据源</strong>这件核心的事情，并<strong>不限制你的具体操作</strong>，切换了数据源可以做任何CRUD。</p>
<p>2.配置文件所有以下划线<code>_</code>分割的数据源<strong>首部</strong>即为组的名称，相同组名称的数据源会放在一个组下。</p>
<p>3.切换数据源可以是组名，也可以是具体数据源名称。组名则切换时采用负载均衡算法切换。</p>
<p>4.默认的数据源名称为<strong>master</strong>，你可以通过<code>spring.datasource.dynamic.primary</code>修改。</p>
<p>5.方法上的注解优先于类上注解。</p>
<p>6.DS支持继承抽象类上的DS，暂不支持继承接口上的DS。</p>
<p>使用：</p>
<p>1.添加依赖</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java">&lt;!--mybatis-plus多数据源动态切换--&gt;<br>&lt;dependency&gt;<br>    &lt;groupId&gt;com.baomidou&lt;/groupId&gt;<br>    &lt;artifactId&gt;dynamic-datasource-spring-boot-starter&lt;/artifactId&gt;<br>    &lt;version&gt;<span class="hljs-number">3.6</span><span class="hljs-number">.1</span>&lt;/version&gt;<br>&lt;/dependency&gt;<br></code></pre></td></tr></table></figure>

<p>2.配置文件</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs yml"><span class="hljs-attr">spring:</span><br>  <span class="hljs-attr">application:</span><br>    <span class="hljs-attr">name:</span> <span class="hljs-string">springbootrud</span><br>  <span class="hljs-attr">datasource:</span><br>    <span class="hljs-attr">dynamic:</span><br>      <span class="hljs-attr">primary:</span> <span class="hljs-string">master</span>  <span class="hljs-comment">#设置默认的数据源或者数据源组,默认值即为master</span><br>      <span class="hljs-attr">strict:</span> <span class="hljs-literal">false</span>  <span class="hljs-comment">#严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源</span><br>      <span class="hljs-attr">datasource:</span><br>        <span class="hljs-attr">master:</span><br>          <span class="hljs-attr">url:</span> <span class="hljs-string">jdbc:mysql://localhost:3306/test01?useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=UTC&amp;useSSL=false</span><br>          <span class="hljs-attr">username:</span> <span class="hljs-string">root</span><br>          <span class="hljs-attr">password:</span> <span class="hljs-string">root</span><br>          <span class="hljs-attr">driverClassName:</span> <span class="hljs-string">com.mysql.cj.jdbc.Driver</span><br>        <span class="hljs-attr">slaver1:</span><br>          <span class="hljs-attr">url:</span> <span class="hljs-string">jdbc:mysql://localhost:3306/test02?useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=UTC&amp;useSSL=false</span><br>          <span class="hljs-attr">username:</span> <span class="hljs-string">root</span><br>          <span class="hljs-attr">password:</span> <span class="hljs-string">root</span><br>          <span class="hljs-attr">driverClassName:</span> <span class="hljs-string">com.mysql.cj.jdbc.Driver</span><br></code></pre></td></tr></table></figure>

<p>3.service中使用@DS()注解</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Override</span><br><span class="hljs-meta">@DS(&quot;master&quot;)</span><br><span class="hljs-keyword">public</span> Staff <span class="hljs-title function_">findStaffById</span><span class="hljs-params">(<span class="hljs-type">int</span> id)</span> &#123;<br><br>    <span class="hljs-keyword">return</span> staffMapper.selectById(id);<br>&#125;<br><br><span class="hljs-meta">@Override</span><br><span class="hljs-meta">@DS(&quot;slaver1&quot;)</span><br><span class="hljs-keyword">public</span> Staff <span class="hljs-title function_">findStaffById2</span><span class="hljs-params">(<span class="hljs-type">int</span> id)</span> &#123;<br><br>    <span class="hljs-keyword">return</span> staffMapper.selectById(id);<br>&#125;<br><br></code></pre></td></tr></table></figure>

<p>使用效果：</p>
<p><img src="https://raw.githubusercontent.com/hellelixi/note-image01/main/images/image-20221228162723597.png" srcset="/hello_lixi.gitee.io/img/loading.gif" lazyload alt="image-20221228162723597"></p>
<p><img src="https://raw.githubusercontent.com/hellelixi/note-image01/main/images/image-20221228162853378.png" srcset="/hello_lixi.gitee.io/img/loading.gif" lazyload alt="image-20221228162853378"></p>
<p>学习使用MindMaster</p>
<p>MindMaster可以理清思路、捕捉灵感、归纳推演、学习和记忆，将纷繁复杂的知识和想法以有序化结构化的方式组织、管理和呈现。</p>
<p>实时mindmaster制作思维导图</p>
<img src="C:/Users/李曦/AppData/Roaming/Typora/typora-user-images/image-20221229165650508.png" srcset="/hello_lixi.gitee.io/img/loading.gif" lazyload alt="image-20221229165650508" style="zoom:80%;" />

<h3 id="1-04-1-07-Redisson实现对Redis数据库读写"><a href="#1-04-1-07-Redisson实现对Redis数据库读写" class="headerlink" title="1.04-1.07 Redisson实现对Redis数据库读写"></a>1.04-1.07 Redisson实现对Redis数据库读写</h3><p>Redis缓存数据库学习，通过Redisson实现对Redis数据库的读写，常用数据类型操作</p>
<p> Redisson - 是一个高级的分布式协调Redis客服端，能帮助用户在分布式环境中轻松实现一些Java的对象，Redisson、Jedis、Lettuce 是三个不同的操作 Redis 的客户端，Jedis、Lettuce 的 API 更侧重对 Reids 数据库的 CRUD（增删改查），而 Redisson API 侧重于分布式开发。</p>
<p>1.导入依赖</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java">&lt;dependency&gt;<br>    &lt;groupId&gt;org.redisson&lt;/groupId&gt;<br>    &lt;artifactId&gt;redisson&lt;/artifactId&gt;<br>    &lt;version&gt;<span class="hljs-number">3.19</span><span class="hljs-number">.0</span>&lt;/version&gt;<br>&lt;/dependency&gt;<br></code></pre></td></tr></table></figure>

<p>2.连接Redis数据库</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">Config</span> <span class="hljs-variable">config</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Config</span>();<br>config.useSingleServer().setAddress(<span class="hljs-string">&quot;redis://127.0.0.1:6379&quot;</span>).setDatabase(<span class="hljs-number">0</span>);<span class="hljs-comment">//指定第几个数据库</span><br><span class="hljs-type">RedissonClient</span> <span class="hljs-variable">redisson</span> <span class="hljs-operator">=</span> Redisson.create(config);<br></code></pre></td></tr></table></figure>

<p>3.对Redis数据库进行操作</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><code class="hljs java"> <span class="hljs-comment">//map</span><br> ConcurrentMap&lt;String,Object&gt; map = redisson.getMap(<span class="hljs-string">&quot;FistMap&quot;</span>,<span class="hljs-keyword">new</span> <span class="hljs-title class_">StringCodec</span>(<span class="hljs-string">&quot;utf-8&quot;</span>));<span class="hljs-comment">//指定编码</span><br> map.put(<span class="hljs-string">&quot;sj&quot;</span>,<span class="hljs-string">&quot;男&quot;</span>);<br> map.put(<span class="hljs-string">&quot;sj55&quot;</span>,<span class="hljs-string">&quot;男&quot;</span>);<br> map.put(<span class="hljs-string">&quot;sj66&quot;</span>,<span class="hljs-string">&quot;男&quot;</span>);<br><br> <span class="hljs-type">ConcurrentMap</span> <span class="hljs-variable">result</span> <span class="hljs-operator">=</span> redisson.getMap(<span class="hljs-string">&quot;FistMap&quot;</span>,<span class="hljs-keyword">new</span> <span class="hljs-title class_">StringCodec</span>(<span class="hljs-string">&quot;utf-8&quot;</span>));<span class="hljs-comment">//存入时指定编码，读取时也要相同的编码，否则会出错</span><br> System.out.println(<span class="hljs-string">&quot;resultmap=&quot;</span>+result.keySet());<br><br> <span class="hljs-comment">//set</span><br> Set myset=redisson.getSet(<span class="hljs-string">&quot;MySet&quot;</span>);<br> myset.add(<span class="hljs-string">&quot;heh&quot;</span>);<br> myset.add(<span class="hljs-string">&quot;yus&quot;</span>);<br> <span class="hljs-type">Set</span> <span class="hljs-variable">setResult</span> <span class="hljs-operator">=</span> redisson.getSet(<span class="hljs-string">&quot;MySet&quot;</span>);<br> System.out.println(<span class="hljs-string">&quot;setResult: &quot;</span>+setResult.toString());<br><br> <span class="hljs-comment">//list</span><br> RList&lt;String&gt; list = redisson.getList(<span class="hljs-string">&quot;list&quot;</span>, <span class="hljs-keyword">new</span> <span class="hljs-title class_">StringCodec</span>(<span class="hljs-string">&quot;utf-8&quot;</span>));<br> list.add(<span class="hljs-string">&quot;徐州&quot;</span>);<br> list.add(<span class="hljs-string">&quot;南京&quot;</span>);<br> System.out.println(<span class="hljs-string">&quot;list: &quot;</span>+list.toString());<br><br><span class="hljs-comment">// queue</span><br> RQueue queue=redisson.getQueue(<span class="hljs-string">&quot;queue&quot;</span>);<br> queue.add(<span class="hljs-string">&quot;12&quot;</span>);<br> queue.add(<span class="hljs-string">&quot;34&quot;</span>);<span class="hljs-comment">//队列满时抛出异常</span><br> queue.offer(<span class="hljs-string">&quot;56&quot;</span>);<span class="hljs-comment">//添加元素，队列满时返回false</span><br> System.out.println(<span class="hljs-string">&quot;queue: &quot;</span>+queue.toString());<br></code></pre></td></tr></table></figure>

<p>4.关闭Redis连接</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">redisson.shutdown();<br></code></pre></td></tr></table></figure>

<p>5.结果</p>
<p><img src="https://raw.githubusercontent.com/hellelixi/note-image01/main/images/image-20230109091258572.png" srcset="/hello_lixi.gitee.io/img/loading.gif" lazyload alt="image-20230109091258572"></p>
<p>掌握Redis Desktop Manager工具使用</p>
<h3 id="1-09-1-15-过Redisson实现Redis消息队列"><a href="#1-09-1-15-过Redisson实现Redis消息队列" class="headerlink" title="1.09-1.15 过Redisson实现Redis消息队列"></a>1.09-1.15 过Redisson实现Redis消息队列</h3><p><strong>通过Redisson实现Redis消息队列使用（至少两个应用程序之间实现生产和消费）</strong></p>
<p>Serializable是一个对象序列化的接口，一个类只有实现了Serializable接口，它的对象才是可序列化的。因此如果要序列化某些类的对象，这些类就必须实现Serializable接口。而实际上，Serializable是一个空接口，没有什么具体内容，它的目的只是简单的标识一个类的对象可以被序列化。</p>
<p>序列化的应用场景</p>
<ol>
<li><p>   比如说内存不够用了，那计算机就要将内存里面的一部分对象暂时的保存到硬盘中，等到要用的时候再读入到内存中，硬盘的那部分存储空间就是所谓的虚拟内存。在比如过你要将某个特定的对象保存到文件中，隔几天在把它拿出来用，那么这时候就要实现Serializable接口。</p>
</li>
<li><p>   在进行Java的Socket编程的时候，有时候可能要传输某一类的对象，那么也就要实现Serializable接口。最常见的你传输一个字符串，它是JDK里面的类，也实现了Serializable接口，这样做为的是将数据变为二进制来传输，所以可以在网络上传输。</p>
</li>
<li><p>   如果要通过远程的方法调用（RMI）去调用一个远程对象的方法，如在计算机A中调用另一台计算机B的对象的方法，那么需要通过JNDI服务获取计算机B目标对象的引用，将对象从B传送到A，就需要实现序列化接口。</p>
</li>
</ol>
<p>注：<br>Serializable这个接口其实是个空接口。接口的注释说明，当让实体类实现Serializable接口时，其实是在告诉JVM此类可被序列化，可被默认的序列化机制序列化，不需要我们实现。</p>
<p><strong>通过Redisson实现Redis消息队列使用，两个服务之间实现生产消费</strong></p>
<p>代码结构</p>
<p><img src="https://raw.githubusercontent.com/hellelixi/note-image01/main/images/image-20230110164002626.png" srcset="/hello_lixi.gitee.io/img/loading.gif" lazyload alt="image-20230110164002626"></p>
<p>1.导入依赖</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java">&lt;dependency&gt;<br>    &lt;groupId&gt;org.redisson&lt;/groupId&gt;<br>    &lt;artifactId&gt;redisson&lt;/artifactId&gt;<br>    &lt;version&gt;<span class="hljs-number">3.19</span><span class="hljs-number">.0</span>&lt;/version&gt;<br>&lt;/dependency&gt;<br></code></pre></td></tr></table></figure>

<p>2.创建redission配置类</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Configuration</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">RedissonConfig</span> &#123;<br>    <span class="hljs-meta">@Bean</span><br>    <span class="hljs-keyword">public</span> RedissonClient <span class="hljs-title function_">config</span><span class="hljs-params">()</span>&#123;<br>        <br>        <span class="hljs-type">Config</span> <span class="hljs-variable">configc</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Config</span>();<br>        configc.useSingleServer().setAddress(<span class="hljs-string">&quot;redis://127.0.0.1:6379&quot;</span>).setDatabase(<span class="hljs-number">0</span>);<br>        <span class="hljs-keyword">return</span> Redisson.create(configc);<br><br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>3.创建消费者</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//Redisson的基于发布-订阅的主题不具备自动监听的功能，需要让消费者继承Spring的ApplicationRunner和Ordered接口，</span><br><span class="hljs-comment">// 使得消费者可以在项目启动后不断监听业务逻辑</span><br><span class="hljs-meta">@Component</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">QueueConsumer</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">ApplicationRunner</span>, Order &#123;<br><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> String queueName=<span class="hljs-string">&quot;redissonName&quot;</span>;<br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> RedissonConfig redissonConfig;<br><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> Class&lt;? <span class="hljs-keyword">extends</span> <span class="hljs-title class_">Annotation</span>&gt; annotationType() &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;<br>    &#125;<br><br>    <span class="hljs-comment">//run方法会在容器启动后执行其中的代码，需要在方法里实现不断监听主题中的消息的动态，间接实现自动监听消费</span><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">run</span><span class="hljs-params">(ApplicationArguments args)</span> <span class="hljs-keyword">throws</span> Exception &#123;<br><br>        RQueue&lt;String&gt; queue= redissonConfig.config().getQueue(queueName);<br>        <span class="hljs-keyword">while</span> (<span class="hljs-literal">true</span>)&#123;<br>            String msg=queue.poll();<br>            <span class="hljs-keyword">if</span> (msg!=<span class="hljs-literal">null</span>)&#123;<br>                System.out.println(<span class="hljs-string">&quot;消息队列监听到消息：&quot;</span>+ msg);<br>            &#125;<br>        &#125;<br><br>    &#125;<br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-type">int</span> <span class="hljs-title function_">value</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>4.创建生产者</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Component</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">QueuePublisher</span> &#123;<br><br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> RedissonConfig config;<br><br>    String key=QueueConsumer.queueName;<br><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">sendMsg</span><span class="hljs-params">(String msg)</span>&#123;<br><br>        <span class="hljs-keyword">try</span> &#123;<br>            RQueue&lt;Object&gt; queue = config.config().getQueue(key);<br>            queue.add(msg);<br>            System.out.println(<span class="hljs-string">&quot;消息队列发送信息成功！&quot;</span>);<br><br>        &#125;<span class="hljs-keyword">catch</span> (Exception e)&#123;<br>            System.out.println(<span class="hljs-string">&quot;消息队列发送信息失败！正在重试&quot;</span>);<br>            RQueue&lt;Object&gt; queue =config.config().getQueue(key);<br>            queue.add(msg);<br>        &#125;<br>        <br>    &#125;<br>    <br>&#125;<br></code></pre></td></tr></table></figure>

<p>5.controller中测试</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//测试redission消息队列</span><br><span class="hljs-meta">@RequestMapping(&quot;/testQueue/&#123;msg&#125;&quot;)</span><br><span class="hljs-keyword">public</span> String <span class="hljs-title function_">queue</span><span class="hljs-params">(<span class="hljs-meta">@PathVariable(&quot;msg&quot;)</span> String msg)</span>&#123;<br>    <span class="hljs-keyword">try</span> &#123;<br>        queue.sendMsg(msg);<br><br>    &#125;<span class="hljs-keyword">catch</span> (Exception e)&#123;<br>        queue.sendMsg(msg);<br>    &#125;<br>    <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;ok&quot;</span>;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>结果：</p>
<img src="C:/Users/李曦/AppData/Roaming/Typora/typora-user-images/image-20230110163316611.png" srcset="/hello_lixi.gitee.io/img/loading.gif" lazyload alt="image-20230110163316611"  />



<p><img src="https://raw.githubusercontent.com/hellelixi/note-image01/main/images/image-20230110163043159.png" srcset="/hello_lixi.gitee.io/img/loading.gif" lazyload alt="image-20230110163043159"></p>
<h3 id="1-16-1-21-Redis多个应用程序之间分布式锁"><a href="#1-16-1-21-Redis多个应用程序之间分布式锁" class="headerlink" title="1.16-1.21 Redis多个应用程序之间分布式锁"></a>1.16-1.21 Redis多个应用程序之间分布式锁</h3><p>Redis缓存数据库学习，实现在多个应用程序之间分布式锁的使用</p>
<p>Redis的分布式锁，使用到redis中的两个命令</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">SETNX key value<br></code></pre></td></tr></table></figure>

<p>setnx 是SET if Not eXists(如果不存在，则 SET)，如果不存在set成功返回int的1，这个key存在了返回0。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">SETEX key seconds value<br></code></pre></td></tr></table></figure>

<p>命令的含义是:</p>
<p>将值 value关 联到 key，并将 key的生存时间设为 seconds (以秒为单位)。</p>
<p>如果key已经存在，setex命令将覆写旧值。</p>
<p>其中setex是一个原子性(atomic)操作，关联值和设置生存时间两个动作会在同一时间内完成。</p>
<p>这两个命令是实现redis分布式锁的关键，同时reddission底层也是通过这两个命令结合lua脚本完成redis分布式锁的。</p>
<p>实现Redis的分布式锁，除了自己基于redis client原生api来实现之外，还可以使用开源框架：Redission</p>
<p>Redisson是一个企业级的开源Redis Client，也提供了分布式锁的支持，如果自己写代码来通过redis设置一个值，是通过下面这个命令设置的。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">SET anyLock unique_value NX PX <span class="hljs-number">30000</span><br></code></pre></td></tr></table></figure>

<p><img src="https://raw.githubusercontent.com/hellelixi/note-image01/main/images/9bcb7f4a7b7c48349ab48bad7ca4f425.png" srcset="/hello_lixi.gitee.io/img/loading.gif" lazyload alt="img"></p>
<p>这里设置的超时时间是30s，假如超过30s都还没有完成业务逻辑的情况下，key会过期，其他线程有可能会获取到锁。</p>
<p>这样一来的话，第一个线程还没执行完业务逻辑，第二个线程进来了也会出现线程安全问题。所以还需要额外的去维护这个过期时间，太麻烦了</p>
<p>而使用redission,只需要通过他的api中的lock和unlock即可完成分布式锁，考虑了很多细节：</p>
<p>redisson所有指令都通过lua脚本执行，redis支持lua脚本原子性执行<br>redisson设置一个key的默认过期时间为30s，如果某个客户端持有一个锁超过了30s怎么办？<br>redisson中有一个watchdog看门狗的概念，它会在获取锁之后，每隔10秒把key的超时时间设为30s</p>
<p>这样的话，就算一直持有锁也不会出现key过期了，其他线程获取到锁的问题了。redisson 的看门狗逻辑保证了没有死锁发生。<br><strong>注意</strong>：如果机器宕机了，看门狗也就没了。此时就不会延长key的过期时间，到了30s之后就会自动过期了，其他线程可以获取到锁)</p>
<p><img src="https://raw.githubusercontent.com/hellelixi/note-image01/main/images/redission1.png" srcset="/hello_lixi.gitee.io/img/loading.gif" lazyload alt="redission1"></p>
<p>代码：</p>
<p>在之前的信息管理项目上进行修改，在管理人员修改员工信息的时候，对该方法进行加锁，同时只能允许一人修改员工信息。</p>
<p>依赖：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.redisson<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>redisson-spring-boot-starter<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>3.19.1<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br><br><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-data-redis<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure>

<p>yml配置信息：</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs yml"><span class="hljs-attr">redis:</span><br>  <span class="hljs-attr">port:</span> <span class="hljs-number">6379</span><br>  <span class="hljs-attr">host:</span> <span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span><br>  <span class="hljs-attr">redisson:</span><br>    <span class="hljs-attr">config:</span><br>      <span class="hljs-attr">singleServerConfig:</span><br>        <span class="hljs-attr">password:</span> <span class="hljs-literal">null</span><br></code></pre></td></tr></table></figure>

<p><strong>StaffServiceImp</strong>：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//对修改信息加锁，同时只允许一人修改</span><br><span class="hljs-meta">@Override</span><br><span class="hljs-keyword">public</span> <span class="hljs-type">int</span> <span class="hljs-title function_">updateStaffInfoById</span><span class="hljs-params">(StaffEntity staff )</span> &#123;<br><br>    RQueue&lt;String&gt; rQueue=redissonClient.getQueue(<span class="hljs-string">&quot;queue2&quot;</span>);<br><br>    RLock anyLock=redissonClient.getLock(<span class="hljs-string">&quot;staff:&quot;</span>+staff.getId());<br>    <span class="hljs-keyword">try</span>&#123;<br>        anyLock.lock(<span class="hljs-number">10</span>, TimeUnit.SECONDS);<br>        <span class="hljs-type">int</span> line=staffMpMapper.updateById(staff);<br>        rQueue.add(staff.getName());<br>        <span class="hljs-keyword">if</span> (line&gt;<span class="hljs-number">0</span>)&#123;<br>            System.out.println(<span class="hljs-string">&quot;线程 &quot;</span>+Thread.currentThread().getId()+<span class="hljs-string">&quot; 修改成功！&quot;</span>);<br>            <span class="hljs-comment">//Thread.sleep(5000000);</span><br>            <span class="hljs-keyword">return</span> line;<br>        &#125;<br>        <span class="hljs-keyword">else</span><br>            System.out.println(<span class="hljs-string">&quot;修改失败11！&quot;</span>);<br>    &#125;<span class="hljs-keyword">catch</span> (Exception e)&#123;<br>        System.out.println(<span class="hljs-string">&quot;修改失败！&quot;</span>);;<br>    &#125;<span class="hljs-keyword">finally</span> &#123;<br>        <span class="hljs-comment">//anyLock.unlock();</span><br>    &#125;<br><br>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>Controller：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@RequestMapping(&quot;/updateStaffInfoById/&#123;name&#125;&quot;)</span><br><span class="hljs-keyword">public</span> String <span class="hljs-title function_">updateStaffInfoById</span><span class="hljs-params">(<span class="hljs-meta">@PathVariable(&quot;name&quot;)</span> String name )</span>&#123;<br><br>    StaffEntity staff=<span class="hljs-keyword">new</span> <span class="hljs-title class_">StaffEntity</span>(<span class="hljs-number">4</span>,<span class="hljs-number">1</span>,name,<span class="hljs-number">26</span>,<span class="hljs-string">&quot;131&quot;</span>,<span class="hljs-string">&quot;在职&quot;</span>);<br>    <span class="hljs-type">int</span> line=staffServiceImp.updateStaffInfoById(staff);<br>    <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;修改 &quot;</span>+line+<span class="hljs-string">&quot; 条记录。 &quot;</span>;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>测试：</p>
<p>当两个进程同时访问修改员工信息时，第一个获取锁的进程可以修改，而第二个则被阻塞，当到达设定的时间之后或者完成逻辑任务，释放锁，第二个进程才能获取锁，执行任务。</p>
<p><img src="%E5%9B%BE%E5%BA%93/image-20230118165823574.png" srcset="/hello_lixi.gitee.io/img/loading.gif" lazyload alt="image-20230118165823574"></p>
<p><img src="https://raw.githubusercontent.com/hellelixi/note-image01/main/images/image-20230118165849107.png" srcset="/hello_lixi.gitee.io/img/loading.gif" lazyload alt="image-20230118165849107"></p>
<p><img src="https://raw.githubusercontent.com/hellelixi/note-image01/main/images/image-20230118165540511-1676358792705.png" srcset="/hello_lixi.gitee.io/img/loading.gif" lazyload alt="image-20230118165540511"></p>
<p>默认 lock() 小结<br>（1）默认指定锁时间为30s（看门狗时间）<br>（2）锁的自动续期：若是业务超长，运行期间自动给锁上新的 30s，不用担心业务时间过长，锁就自动过期<br>（3）加锁的业务只要运行完成，就不会给当前锁续期，及时不手动解锁，锁默认在30s 后自动删除。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 10s自动解锁，指定时间一定要大于业务时间（不然会报错，没把握就不要用）</span><br>lock.lock (<span class="hljs-number">10</span>, TimeUnit.SECONDS);<br></code></pre></td></tr></table></figure>

<p>指定时间 lock() 小结<br>问题：在锁到的时候，不会自动续期。<br>（1）如果我们传递了锁的超时时间，就发送给 redis执行脚本，进行占锁，默认的超时时间既我们指的时间<br>（2）若是未指定锁的超时时间，就使用 30*1000【LockWatchdogTimeout看门狗的默认时间】<br>（3）只要占锁成功，就会启动一个定时任务【重新给锁设置过期时间，新的过期时间就是看门狗的默认时间】，每隔 10 s都会自动再次续到30s, internallockLeaseTime【看门狗时间/3s】</p>
<p><strong>redisson提供的几种锁：</strong></p>
<p>可重入锁（Reentrant Lock）<br>Redisson的分布式可重入锁 RLock Java对象实现了java.util.concurrent.locks.Lock接口，同时还支持自动过期解锁。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testReentrantLock</span><span class="hljs-params">(RedissonClient redisson)</span>&#123;    <br>    <span class="hljs-type">RLock</span> <span class="hljs-variable">lock</span> <span class="hljs-operator">=</span> redisson.getLock(<span class="hljs-string">&quot;anyLock&quot;</span>);<br>    <span class="hljs-keyword">try</span>&#123;<br>        <span class="hljs-comment">// 1. 最常见的使用方法</span><br>        <span class="hljs-comment">//lock.lock();</span><br> <br>        <span class="hljs-comment">// 2. 支持过期解锁功能,10秒钟以后自动解锁, 无需调用unlock方法手动解锁</span><br>        <span class="hljs-comment">//lock.lock(10, TimeUnit.SECONDS);</span><br> <br>        <span class="hljs-comment">// 3. 尝试加锁，最多等待3秒，上锁以后10秒自动解锁</span><br>        <span class="hljs-type">boolean</span> <span class="hljs-variable">res</span> <span class="hljs-operator">=</span> lock.tryLock(<span class="hljs-number">3</span>, <span class="hljs-number">10</span>, TimeUnit.SECONDS);<br>        <span class="hljs-keyword">if</span>(res)&#123;    <span class="hljs-comment">//成功</span><br>            <span class="hljs-comment">// do your business</span><br> <br>        &#125;<br>    &#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;<br>        e.printStackTrace();<br>    &#125; <span class="hljs-keyword">finally</span> &#123;<br>        lock.unlock();<br>    &#125;<br> <br>&#125;<br></code></pre></td></tr></table></figure>
<p>Redisson同时还为分布式锁提供了异步执行的相关方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testAsyncReentrantLock</span><span class="hljs-params">(RedissonClient redisson)</span>&#123;<br>        <span class="hljs-type">RLock</span> <span class="hljs-variable">lock</span> <span class="hljs-operator">=</span> redisson.getLock(<span class="hljs-string">&quot;anyLock&quot;</span>);<br>        <span class="hljs-keyword">try</span>&#123;<br>            lock.lockAsync();<br>            lock.lockAsync(<span class="hljs-number">10</span>, TimeUnit.SECONDS);<br>            Future&lt;Boolean&gt; res = lock.tryLockAsync(<span class="hljs-number">3</span>, <span class="hljs-number">10</span>, TimeUnit.SECONDS);        <br>		    <span class="hljs-keyword">if</span>(res.get())&#123;<br>               <span class="hljs-comment">// do your business</span><br> <br>        &#125;<br>    &#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;<br>        e.printStackTrace();<br>    &#125; <span class="hljs-keyword">catch</span> (ExecutionException e) &#123;<br>        e.printStackTrace();<br>    &#125; <span class="hljs-keyword">finally</span> &#123;<br>        lock.unlock();<br>    &#125;<br> <br>&#125;<br></code></pre></td></tr></table></figure>


<p>公平锁（Fair Lock）<br>Redisson分布式可重入公平锁也是实现了java.util.concurrent.locks.Lock接口的一种RLock对象。在提供了自动过期解锁功能的同时，保证了当多个Redisson客户端线程同时请求加锁时，优先分配给先发出请求的线程。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testFairLock</span><span class="hljs-params">(RedissonClient redisson)</span>&#123;<br><br>    <span class="hljs-type">RLock</span> <span class="hljs-variable">fairLock</span> <span class="hljs-operator">=</span> redisson.getFairLock(<span class="hljs-string">&quot;anyLock&quot;</span>);<br>    <span class="hljs-keyword">try</span>&#123;<br>        <span class="hljs-comment">// 最常见的使用方法</span><br>        fairLock.lock();<br><br>        <span class="hljs-comment">// 支持过期解锁功能, 10秒钟以后自动解锁,无需调用unlock方法手动解锁</span><br>        fairLock.lock(<span class="hljs-number">10</span>, TimeUnit.SECONDS);<br><br>        <span class="hljs-comment">// 尝试加锁，最多等待100秒，上锁以后10秒自动解锁</span><br>        <span class="hljs-type">boolean</span> <span class="hljs-variable">res</span> <span class="hljs-operator">=</span> fairLock.tryLock(<span class="hljs-number">100</span>, <span class="hljs-number">10</span>, TimeUnit.SECONDS);<br>    &#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;<br>        e.printStackTrace();<br>    &#125; <span class="hljs-keyword">finally</span> &#123;<br>        fairLock.unlock();<br>    &#125;<br><br>&#125;<br></code></pre></td></tr></table></figure>

<p>Redisson同时还为分布式可重入公平锁提供了异步执行的相关方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">RLock</span> <span class="hljs-variable">fairLock</span> <span class="hljs-operator">=</span> redisson.getFairLock(<span class="hljs-string">&quot;anyLock&quot;</span>);<br>fairLock.lockAsync();<br>fairLock.lockAsync(<span class="hljs-number">10</span>, TimeUnit.SECONDS);<br>Future&lt;Boolean&gt; res = fairLock.tryLockAsync(<span class="hljs-number">100</span>, <span class="hljs-number">10</span>, TimeUnit.SECONDS);<br></code></pre></td></tr></table></figure>

<p>联锁（MultiLock）</p>
<p>Redisson的RedissonMultiLock对象可以将多个RLock对象关联为一个联锁，每个RLock对象实例可以来自于不同的Redisson实例。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testMultiLock</span><span class="hljs-params">(RedissonClient redisson1,</span><br><span class="hljs-params">                                RedissonClient redisson2, RedissonClient redisson3)</span>&#123;<br><br>      <span class="hljs-type">RLock</span> <span class="hljs-variable">lock1</span> <span class="hljs-operator">=</span> redisson1.getLock(<span class="hljs-string">&quot;lock1&quot;</span>);<br>      <span class="hljs-type">RLock</span> <span class="hljs-variable">lock2</span> <span class="hljs-operator">=</span> redisson2.getLock(<span class="hljs-string">&quot;lock2&quot;</span>);<br>      <span class="hljs-type">RLock</span> <span class="hljs-variable">lock3</span> <span class="hljs-operator">=</span> redisson3.getLock(<span class="hljs-string">&quot;lock3&quot;</span>);<br><br>      <span class="hljs-type">RedissonMultiLock</span> <span class="hljs-variable">lock</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RedissonMultiLock</span>(lock1, lock2, lock3);<br><br>      <span class="hljs-keyword">try</span> &#123;<br>          <span class="hljs-comment">// 同时加锁：lock1 lock2 lock3, 所有的锁都上锁成功才算成功。</span><br>          lock.lock();<br><br>          <span class="hljs-comment">// 尝试加锁，最多等待100秒，上锁以后10秒自动解锁</span><br>          <span class="hljs-type">boolean</span> <span class="hljs-variable">res</span> <span class="hljs-operator">=</span> lock.tryLock(<span class="hljs-number">100</span>, <span class="hljs-number">10</span>, TimeUnit.SECONDS);<br><br>      &#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;<br>          e.printStackTrace();<br>      &#125; <span class="hljs-keyword">finally</span> &#123;<br>          lock.unlock();<br>      &#125;<br><br>  &#125;<br></code></pre></td></tr></table></figure>

<p>红锁（RedLock）<br>Redisson的RedissonRedLock对象实现了Redlock介绍的加锁算法。该对象也可以用来将多个RLock<br>对象关联为一个红锁，每个RLock对象实例可以来自于不同的Redisson实例。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testRedLock</span><span class="hljs-params">(RedissonClient redisson1,</span><br><span class="hljs-params">                          RedissonClient redisson2, RedissonClient redisson3)</span>&#123;<br> <br>    <span class="hljs-type">RLock</span> <span class="hljs-variable">lock1</span> <span class="hljs-operator">=</span> redisson1.getLock(<span class="hljs-string">&quot;lock1&quot;</span>);<br>    <span class="hljs-type">RLock</span> <span class="hljs-variable">lock2</span> <span class="hljs-operator">=</span> redisson2.getLock(<span class="hljs-string">&quot;lock2&quot;</span>);<br>    <span class="hljs-type">RLock</span> <span class="hljs-variable">lock3</span> <span class="hljs-operator">=</span> redisson3.getLock(<span class="hljs-string">&quot;lock3&quot;</span>);<br> <br>    <span class="hljs-type">RedissonRedLock</span> <span class="hljs-variable">lock</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RedissonRedLock</span>(lock1, lock2, lock3);<br>  <span class="hljs-keyword">try</span> &#123;<br>        <span class="hljs-comment">// 同时加锁：lock1 lock2 lock3, 红锁在大部分节点上加锁成功就算成功。</span><br>        lock.lock();<br> <br>        <span class="hljs-comment">// 尝试加锁，最多等待100秒，上锁以后10秒自动解锁</span><br>        <span class="hljs-type">boolean</span> <span class="hljs-variable">res</span> <span class="hljs-operator">=</span> lock.tryLock(<span class="hljs-number">100</span>, <span class="hljs-number">10</span>, TimeUnit.SECONDS);<br> <br>    &#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;<br>        e.printStackTrace();<br>    &#125; <span class="hljs-keyword">finally</span> &#123;<br>        lock.unlock();<br>    &#125;<br> <br>&#125;<br></code></pre></td></tr></table></figure>
<p>读写锁（ReadWriteLock）<br>Redisson的分布式可重入读写锁RReadWriteLock Java对象实现了java.util.concurrent.locks.ReadWriteLock接口。同时还支持自动过期解锁。该对象允许同时有多个读取锁，但是最多只能有一个写入锁。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">RReadWriteLock</span> <span class="hljs-variable">rwlock</span> <span class="hljs-operator">=</span> redisson.getLock(<span class="hljs-string">&quot;anyRWLock&quot;</span>);<br><span class="hljs-comment">// 最常见的使用方法</span><br>rwlock.readLock().lock();<br><span class="hljs-comment">// 或</span><br>rwlock.writeLock().lock();<br><br><span class="hljs-comment">// 支持过期解锁功能</span><br><span class="hljs-comment">// 10秒钟以后自动解锁</span><br><span class="hljs-comment">// 无需调用unlock方法手动解锁</span><br>rwlock.readLock().lock(<span class="hljs-number">10</span>, TimeUnit.SECONDS);<br><span class="hljs-comment">// 或</span><br>rwlock.writeLock().lock(<span class="hljs-number">10</span>, TimeUnit.SECONDS);<br><br><span class="hljs-comment">// 尝试加锁，最多等待100秒，上锁以后10秒自动解锁</span><br><span class="hljs-type">boolean</span> <span class="hljs-variable">res</span> <span class="hljs-operator">=</span> rwlock.readLock().tryLock(<span class="hljs-number">100</span>, <span class="hljs-number">10</span>, TimeUnit.SECONDS);<br><span class="hljs-comment">// 或</span><br><span class="hljs-type">boolean</span> <span class="hljs-variable">res</span> <span class="hljs-operator">=</span> rwlock.writeLock().tryLock(<span class="hljs-number">100</span>, <span class="hljs-number">10</span>, TimeUnit.SECONDS);<br>...<br>lock.unlock();<br></code></pre></td></tr></table></figure>



<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@RestController</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">IndexController</span> &#123;<br><br>    <span class="hljs-meta">@Resource</span><br>    <span class="hljs-keyword">private</span> Redisson redisson;<br><br>    <span class="hljs-meta">@Resource</span><br>    <span class="hljs-keyword">private</span> StringRedisTemplate stringRedisTemplate; <span class="hljs-comment">// 居然根据名字来找的，不是根据类型。。。</span><br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 代码有什么问题？</span><br><span class="hljs-comment">     *      1.线程安全问题，多个线程同时访问就可能出现错误，用synchronized可以解决但是性能不好</span><br><span class="hljs-comment">     *          synchronized在高并发情况下还是有bug出现，会出现超卖，可以用jmeter压测</span><br><span class="hljs-comment">     *      2.设置redis锁解决分布式场景之后，超时时间设置10s合理吗？适合场景问题？如果10s中之内没有处理完，处理到一半呢？</span><br><span class="hljs-comment">     *          15s--10s后释放锁--还需要5s，5s后释放了其他人的锁</span><br><span class="hljs-comment">     *          8s--5s后我的锁被人释放了，其他线程又来了</span><br><span class="hljs-comment">     *          循环下去，锁的是别人....这不就完全乱套了，这锁完全没用啊</span><br><span class="hljs-comment">     *        解决方法：你不是可能存在释放别人的锁的情况吗？那就设置识别号，识别到只能是自己的才能被释放</span><br><span class="hljs-comment">     *        这只是解决了释放别人的锁的问题，你自己没有执行完就已经超时的问题呢？</span><br><span class="hljs-comment">     *        答案：开启子线程定时器来延长超时时间咯，子线程每隔一段时间就去查看是否完成，没完成就加时，那这个一段时间要多长呢？</span><br><span class="hljs-comment">     *             三分之一过期时间，其他人的实践经验。</span><br><span class="hljs-comment">     *        所以：我们现在又要造轮子了吗？是否有其他人已经考虑过这个问题并做开源了呢？</span><br><span class="hljs-comment">     *        那肯定有啊，不然我写这个干吗。redisson，比jedis强大，专对分布式</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     *      3.redisson</span><br><span class="hljs-comment">     *          大概阐述一下这个锁的操作：</span><br><span class="hljs-comment">     *          当一个redisson线程过来获取到锁时，后台会有其他线程去检查是否还持有锁，</span><br><span class="hljs-comment">     *          还持有说明还没执行结束，就会继续延长锁的时间，大概10s去轮询。（三分之一）</span><br><span class="hljs-comment">     *          另外一个线程过来，如果没有获取锁成功，就会while自旋尝试加锁。</span><br><span class="hljs-comment">     *          clientId他在底层实现了。</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     *          3.1如果使用的是Redis主从架构呢，主节点宕了，从节点怎么处理？但这是锁还没有被同步过去，其他线程就过来访问了呢？</span><br><span class="hljs-comment">     *          3.2另外如何提升性能呢？</span><br><span class="hljs-comment">     *              - 商品库存分段存储，key不一样，每个段的数量越小性能不就越高嘛，而且锁定不同的key值</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span></span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-meta">@RequestMapping(&quot;/deduct_stock&quot;)</span><br>    <span class="hljs-keyword">public</span> String <span class="hljs-title function_">deductStock</span><span class="hljs-params">()</span> &#123;<br><br><br>        <span class="hljs-comment">// 0.标识号</span><br>        <span class="hljs-type">String</span> <span class="hljs-variable">clientID</span> <span class="hljs-operator">=</span> UUID.randomUUID().toString();<br><br>        <span class="hljs-comment">// 1.这个相当于一把锁，控制只能一个人来</span><br>        <span class="hljs-type">String</span> <span class="hljs-variable">lockKey</span> <span class="hljs-operator">=</span> <span class="hljs-string">&quot;product001&quot;</span>;<br>        <span class="hljs-type">RLock</span> <span class="hljs-variable">lock</span> <span class="hljs-operator">=</span> redisson.getLock(lockKey); <span class="hljs-comment">// 3.1</span><br>        <span class="hljs-keyword">try</span>&#123;<br><span class="hljs-comment">/*</span><br><span class="hljs-comment">            // 2.获取锁</span><br><span class="hljs-comment">            Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, &quot;yjiewei&quot;);// jedis.setnx(key, value);</span><br><span class="hljs-comment">            // 3.如果突发宕机，锁没有释放掉怎么办，key过期处理(10s)，但是如果在获取锁之后就出问题呢，这一步也没有成功，大招：二合一</span><br><span class="hljs-comment">            stringRedisTemplate.expire(lockKey, 10, TimeUnit.SECONDS);</span><br><span class="hljs-comment">*/</span><br><br>            <span class="hljs-comment">// 2-3</span><br><span class="hljs-comment">/*          Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, clientID, 10, TimeUnit.SECONDS);</span><br><span class="hljs-comment">            if (!result)&#123;</span><br><span class="hljs-comment">                return &quot;error&quot;;</span><br><span class="hljs-comment">            &#125;</span><br><span class="hljs-comment">*/</span><br><br>            <span class="hljs-comment">// 3.1 解决过期时间内还未完成操作的问题</span><br>            lock.lock(<span class="hljs-number">30</span>, TimeUnit.SECONDS); <span class="hljs-comment">// 先拿锁，再设置超时时间</span><br><br>            <span class="hljs-comment">// 4.真正操作商品库存</span><br>            <span class="hljs-keyword">synchronized</span> (<span class="hljs-built_in">this</span>)&#123;<br>                <span class="hljs-type">int</span> <span class="hljs-variable">stock</span> <span class="hljs-operator">=</span> Integer.parseInt(stringRedisTemplate.opsForValue().get(<span class="hljs-string">&quot;stock&quot;</span>)); <span class="hljs-comment">// jedis.get(&quot;stock&quot;);</span><br>                <span class="hljs-keyword">if</span> (stock &gt; <span class="hljs-number">0</span>)&#123;<br>                    <span class="hljs-type">int</span> <span class="hljs-variable">realStock</span> <span class="hljs-operator">=</span> stock - <span class="hljs-number">1</span>;<br>                    stringRedisTemplate.opsForValue().set(<span class="hljs-string">&quot;stock&quot;</span>, realStock + <span class="hljs-string">&quot;&quot;</span>); <span class="hljs-comment">// jedis.set(key, value);</span><br>                    System.out.println(<span class="hljs-string">&quot;扣减成功，剩余库存：&quot;</span> + realStock);<br>                &#125;<span class="hljs-keyword">else</span> &#123;<br>                    System.out.println(<span class="hljs-string">&quot;扣减失败，库存不足！&quot;</span>);<br>                &#125;<br>            &#125;<br>        &#125;<span class="hljs-keyword">finally</span> &#123;<br>            lock.unlock(); <span class="hljs-comment">// 释放锁</span><br><span class="hljs-comment">/*</span><br><span class="hljs-comment">            if (clientID.equals(stringRedisTemplate.opsForValue().get(lockKey))) &#123;</span><br><span class="hljs-comment">                // 5.释放锁，放在finally块中防止没有释放锁导致死锁问题</span><br><span class="hljs-comment">                stringRedisTemplate.delete(lockKey);</span><br><span class="hljs-comment">            &#125;</span><br><span class="hljs-comment">*/</span><br>        &#125;<br>        <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;end&quot;</span>;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>
                
              </div>
            
            <hr/>
            <div>
              <div class="post-metas my-3">
  
    <div class="post-meta mr-3 d-flex align-items-center">
      <i class="iconfont icon-category"></i>
      

<span class="category-chains">
  
  
    
      <span class="category-chain">
        
  <a href="/hello_lixi.gitee.io/categories/Java%E5%BC%80%E5%8F%91/" class="category-chain-item">Java开发</a>
  
  

      </span>
    
  
</span>

    </div>
  
  
    <div class="post-meta">
      <i class="iconfont icon-tags"></i>
      
        <a href="/hello_lixi.gitee.io/tags/Java%E5%BC%80%E5%8F%91%E5%AD%A6%E4%B9%A0/">#Java开发学习</a>
      
    </div>
  
</div>


              
  

  <div class="license-box my-3">
    <div class="license-title">
      <div>2022.11-2023.1 Java开发学习</div>
      <div>https://xistudycode.gitee.io/hello_lixi.gitee.io/2023/02/14/学习任务总结/</div>
    </div>
    <div class="license-meta">
      
        <div class="license-meta-item">
          <div>作者</div>
          <div>Mr.LI</div>
        </div>
      
      
        <div class="license-meta-item license-meta-date">
          <div>发布于</div>
          <div>2023年2月14日</div>
        </div>
      
      
      
        <div class="license-meta-item">
          <div>许可协议</div>
          <div>
            
              
              
                <a target="_blank" href="https://creativecommons.org/licenses/by/4.0/">
                  <span class="hint--top hint--rounded" aria-label="BY - 署名">
                    <i class="iconfont icon-by"></i>
                  </span>
                </a>
              
            
          </div>
        </div>
      
    </div>
    <div class="license-icon iconfont"></div>
  </div>



              
                <div class="post-prevnext my-3">
                  <article class="post-prev col-6">
                    
                    
                      <a href="/hello_lixi.gitee.io/2023/04/10/%E8%B7%A8%E5%9F%9F%E5%A4%84%E7%90%86/" title="跨域处理问题">
                        <i class="iconfont icon-arrowleft"></i>
                        <span class="hidden-mobile">跨域处理问题</span>
                        <span class="visible-mobile">上一篇</span>
                      </a>
                    
                  </article>
                  <article class="post-next col-6">
                    
                    
                      <a href="/hello_lixi.gitee.io/2023/02/10/SpringCloud%E9%A1%B9%E7%9B%AE%E9%9B%86%E6%88%90nacos%E5%90%AF%E5%8A%A8%E6%8A%A5%E9%94%99/" title="SpringCloud项目集成nacos启动报错,Failed to configure a DataSource url attribute is not specified and no embedded datasource could be configured">
                        <span class="hidden-mobile">SpringCloud项目集成nacos启动报错,Failed to configure a DataSource url attribute is not specified and no embedded datasource could be configured</span>
                        <span class="visible-mobile">下一篇</span>
                        <i class="iconfont icon-arrowright"></i>
                      </a>
                    
                  </article>
                </div>
              
            </div>

            
          </article>
        </div>
      </div>
    </div>

    <div class="side-col d-none d-lg-block col-lg-2">
      
  <aside class="sidebar" style="margin-left: -1rem">
    <div id="toc">
  <p class="toc-header"><i class="iconfont icon-list"></i>&nbsp;目录</p>
  <div class="toc-body" id="toc-body"></div>
</div>



  </aside>


    </div>
  </div>
</div>





  



  



  



  



  







    

    
      <a id="scroll-top-button" aria-label="TOP" href="#" role="button">
        <i class="iconfont icon-arrowup" aria-hidden="true"></i>
      </a>
    

    
      <div class="modal fade" id="modalSearch" tabindex="-1" role="dialog" aria-labelledby="ModalLabel"
     aria-hidden="true">
  <div class="modal-dialog modal-dialog-scrollable modal-lg" role="document">
    <div class="modal-content">
      <div class="modal-header text-center">
        <h4 class="modal-title w-100 font-weight-bold">搜索</h4>
        <button type="button" id="local-search-close" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body mx-3">
        <div class="md-form mb-5">
          <input type="text" id="local-search-input" class="form-control validate">
          <label data-error="x" data-success="v" for="local-search-input">关键词</label>
        </div>
        <div class="list-group" id="local-search-result"></div>
      </div>
    </div>
  </div>
</div>

    

    
  </main>

  <footer>
    <div class="footer-inner">
  
    <div class="footer-content">
       <a href="https://hexo.io" target="_blank" rel="nofollow noopener"><span>xBolg</span></a> <i class="iconfont icon-love"></i> <a href="https://github.com/fluid-dev/hexo-theme-fluid" target="_blank" rel="nofollow noopener"><span>@Fluid</span></a> 
    </div>
  
  
    <div class="statistics">
  
  

  
    
      <span id="busuanzi_container_site_pv" style="display: none">
        总访问量 
        <span id="busuanzi_value_site_pv"></span>
         次
      </span>
    
    
      <span id="busuanzi_container_site_uv" style="display: none">
        总访客数 
        <span id="busuanzi_value_site_uv"></span>
         人
      </span>
    
    
  
</div>

  
  
  
</div>

  </footer>

  <!-- Scripts -->
  
  <script  src="https://lib.baomitu.com/nprogress/0.2.0/nprogress.min.js" ></script>
  <link  rel="stylesheet" href="https://lib.baomitu.com/nprogress/0.2.0/nprogress.min.css" />

  <script>
    NProgress.configure({"showSpinner":false,"trickleSpeed":100})
    NProgress.start()
    window.addEventListener('load', function() {
      NProgress.done();
    })
  </script>


<script  src="https://lib.baomitu.com/jquery/3.6.0/jquery.min.js" ></script>
<script  src="https://lib.baomitu.com/twitter-bootstrap/4.6.1/js/bootstrap.min.js" ></script>
<script  src="/hello_lixi.gitee.io/js/events.js" ></script>
<script  src="/hello_lixi.gitee.io/js/plugins.js" ></script>


  <script  src="https://lib.baomitu.com/typed.js/2.0.12/typed.min.js" ></script>
  <script>
    (function (window, document) {
      var typing = Fluid.plugins.typing;
      var subtitle = document.getElementById('subtitle');
      if (!subtitle || !typing) {
        return;
      }
      var text = subtitle.getAttribute('data-typed-text');
      
        typing(text);
      
    })(window, document);
  </script>




  
    <script  src="/hello_lixi.gitee.io/js/img-lazyload.js" ></script>
  




  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/tocbot/4.18.2/tocbot.min.js', function() {
    var toc = jQuery('#toc');
    if (toc.length === 0 || !window.tocbot) { return; }
    var boardCtn = jQuery('#board-ctn');
    var boardTop = boardCtn.offset().top;

    window.tocbot.init(Object.assign({
      tocSelector     : '#toc-body',
      contentSelector : '.markdown-body',
      linkClass       : 'tocbot-link',
      activeLinkClass : 'tocbot-active-link',
      listClass       : 'tocbot-list',
      isCollapsedClass: 'tocbot-is-collapsed',
      collapsibleClass: 'tocbot-is-collapsible',
      scrollSmooth    : true,
      includeTitleTags: true,
      headingsOffset  : -boardTop,
    }, CONFIG.toc));
    if (toc.find('.toc-list-item').length > 0) {
      toc.css('visibility', 'visible');
    }

    Fluid.events.registerRefreshCallback(function() {
      if ('tocbot' in window) {
        tocbot.refresh();
        var toc = jQuery('#toc');
        if (toc.length === 0 || !tocbot) {
          return;
        }
        if (toc.find('.toc-list-item').length > 0) {
          toc.css('visibility', 'visible');
        }
      }
    });
  });
</script>


  <script src=https://lib.baomitu.com/clipboard.js/2.0.11/clipboard.min.js></script>

  <script>Fluid.plugins.codeWidget();</script>


  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/anchor-js/4.3.1/anchor.min.js', function() {
    window.anchors.options = {
      placement: CONFIG.anchorjs.placement,
      visible  : CONFIG.anchorjs.visible
    };
    if (CONFIG.anchorjs.icon) {
      window.anchors.options.icon = CONFIG.anchorjs.icon;
    }
    var el = (CONFIG.anchorjs.element || 'h1,h2,h3,h4,h5,h6').split(',');
    var res = [];
    for (var item of el) {
      res.push('.markdown-body > ' + item.trim());
    }
    if (CONFIG.anchorjs.placement === 'left') {
      window.anchors.options.class = 'anchorjs-link-left';
    }
    window.anchors.add(res.join(', '));

    Fluid.events.registerRefreshCallback(function() {
      if ('anchors' in window) {
        anchors.removeAll();
        var el = (CONFIG.anchorjs.element || 'h1,h2,h3,h4,h5,h6').split(',');
        var res = [];
        for (var item of el) {
          res.push('.markdown-body > ' + item.trim());
        }
        if (CONFIG.anchorjs.placement === 'left') {
          anchors.options.class = 'anchorjs-link-left';
        }
        anchors.add(res.join(', '));
      }
    });
  });
</script>


  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.js', function() {
    Fluid.plugins.fancyBox();
  });
</script>


  <script>Fluid.plugins.imageCaption();</script>

  <script  src="/hello_lixi.gitee.io/js/local-search.js" ></script>

  <script defer src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js" ></script>





<!-- 主题的启动项，将它保持在最底部 -->
<!-- the boot of the theme, keep it at the bottom -->
<script  src="/hello_lixi.gitee.io/js/boot.js" ></script>


  

  <noscript>
    <div class="noscript-warning">博客在允许 JavaScript 运行的环境下浏览效果更佳</div>
  </noscript>
</body>
</html>
